I switched to fish a few years back after a couple decades of using bash. It took about 10 minutes to make that permanent everywhere – because so much is built-in and the defaults are good, there was no need to spend time on more than the package install plus chsh.
For scripting, I’d already set a personal policy that anything complicated use Python. For me there’s a fairly big difference between interactive shell sessions and scripts which solidly shifts the balance to a full language with a rich standard library and robust error handling. My scripts were edited using shfmt and shellcheck interactively anyway, so switching to Python wasn’t an increase in terms of tool support and the richer language means that I’m writing less code to get more functionality and especially error handling.
Same here. It's just so ergonomic. For instance, here's how to make a fancy prompt in fish:
- Write a function called "fish_prompt" with "echo", "set_color", and whatever else you want.
- Put it in a file in fish's "functions" directory called "fish_prompt.fish"
That's it. Done. Every time fish needs to display a prompt, it does so by calling the "fish_prompt" function. Where does fish find the definition of a function called "foo"? In a file called "foo.fish".
Oh, do you want one of those very cool right-side-of-the-window prompts showing the date, maybe? Write a function called "fish_right_prompt.fish". There, you're finished.
There are a million little niceties like this that seem so obvious in retrospect. Fish is what happens when someone completely rethinks a shell from the perspective of how things should be done today after we have a few decades of experimentation under our belt.
I’m glad that works for you but that sounds like a horrible solution to me. Maybe better than Bash but magic named files are probably my least favourite feature in scripting languages.
Don't think of it as magic, but as convention. Where do you put your function definitions? In the folder named "functions". There are only a handful of such things to learn.
In fish, you put the functions inside function blocks. There's nothing at all magical about that. However, if you write "function foo" inside a file named "foo.fish", then type the command "foo" at the shell prompt, then fish will look for a file named "foo.fish" and then execute the function "foo" defined inside it. That's its autoload mechanism.
That's the only remotely magical thing added here: by convention, an autoload function named "foo" is defined in ~/.config/fish/functions/foo.fish.
Your .bashrc, .zshrc, .profile, etc. files are also special. All that's going on is that there is folder where you can leave functions to be included in the main namespace, I don't see the big difference.
The difference as I understand it is your Fish equivalent of a $PS1 (and other Fish shell behaviours) have to be defined via that path. If I understand it correctly, it sounds a lot like git hooks but at a global scale rather than per repo.
Now I’ve got nothing against git hooks nor Fish per se, I just don’t see this particular model for defining behaviour to be convenient (eg what if you want to quickly change the prompt of a session without affecting other sessions?)
There might be more detail I’m missing and if that’s the case I apologise. But from what I’ve read thus far I’m not sold. It might appeal to others and good for them.
How would you normally do it? Because you can still put redefinitions of all the standard functions into a single file and load them with `.` as you would with other shells.
Well I’m not suggesting the following is a better alt shell, but in murex you have something that’s a little bit akin to a Windows Registry in that all of the shell settings are navigable through a builtin called ‘config’
You can set prompt functions with that; strings, ints and Booleans too. And ‘config’ comes with descriptions for each configurable thing, choices of options in many cases, and an easy way to default back to shell defaults too. So it’s dead easy to play around configuring whatever you want (you never need to leave the shell to look up an option).
The shell has its own problems though but it’s an interesting alternative take for grouping shell config.
>The difference as I understand it is your Fish equivalent of a $PS1 (and other Fish shell behaviours) have to be defined via that path.
They don't. You have to define the function somehow, but fish doesn't care about how that happens. It's just that if it's not defined yet it'll try to load it from the file.
If you want, you can just do it all in config.fish, like you'd do it all in .bashrc for bash.
>what if you want to quickly change the prompt of a session without affecting other sessions?
Then you can redefine the function, just like you could switch the value of $PS1.
I was going to mount a defense of fish about how it doesn't provide the same mechanisms or provide the same incentives that had people contorting Ruby into the most convoluted forms on their way to spit out HTML or JSON from an HTTP request, but then I found that the thing has a `fish_command_not_found` that you can overload as in Ruby.
You could make it run arbitrary things, but then you're quite clearly misusing it and e.g. there's no way to make it return a different status, fish will still view the command as failed.
Okay so I think I understand your objection a bit more. So fish only wants you to define a function called fish_prompt in order to customize the prompt. That function can be defined however you want. It can be in fish’s equivalent of .bashrc (~/.config/fish/config.fish). Putting it in ~/.config/fish/functions/fish_prompt.fish is just leveraging more tooling around shell function autoloading and sane defaults.
You can achieve PS1-like functionality “simply” by placing this bit of code somewhere in your startup:
function fish_prompt
set -l prompt_symbol '$'
fish_is_root_user; and set prompt_symbol '#'
echo -s $hostname (set_color blue) (prompt_pwd) \
(set_color yellow) $prompt_symbol (set_color normal)
end
It’s very verbose but it runs as fast as both bash’s PS1 and zsh’s PROMPT and is much easier for someone new to fussing with prompts to comprehend.
Even as someone who doesn’t use fish day to day, I think fish’s prompt customization is much more approachable. If I had a powerline prompt I wanted to tweak, I’d know to start with `functions fish_prompt` to print out the current contents of the function and have the complete code to how the prompt is built. zsh requires a lot more reading and understanding of the prompt system before you can dig into how exactly it all works. IIRC, most dynamic bash prompts have similar issues. PS1 is just interpolated globals and color codes, creating a disconnect between the prompt variable and the shell functions that update it.
Fish doesn't work with a light theme. (Dark text on white background.) Also the keybindings in fish are extremely unergonomic.
Honestly I just want a bash that saves every command entered, ever, and never deletes history. (I think I can spare the 100 kilobytes of disk space to hold command history forever.)
And fish doesn't get this right either, even though it's a complete no-brainer.
Sigh.
`fish` is amazing. I used to have a big old dotfiles repo, but nowadays I add starship[0] to my env and I'm pretty much good to go.
On the non-interactive scripting front, I've found fish to be a very competent language for it, but you're right, the error handling (among other things) are lacking compared to "real" languages
I have to say, I use fish and I customize my prompt... is starship really necessary? I recognize starship is cross-shell, but if I standardize on installing fish on my machines, I'm not sure I need that feature.
Is there a "killer app" for starship that you can't get from writing out your prompt in a fish shell script?
It's a 3-line prompt with a timestamp on the first line, my username@host + pwd on the 2nd line, and the actual prompt with the cursor on the 3rd line. I'm now considering swapping the timestamp to the right side after that possibility was mentioned in other comments in this thread.
I used fish for around a year and a half and wound up switching back to zsh. fish's startup time is way too long for my tastes, and the devs seem completely uninterested in providing a "don't load plugins" flag. I open up new tmux panes/windows/etc all the time, so fish's slowness here is really noticeable. It also negatively affects using things like `tmux popup`, which spawns a shell. Things like this that take no noticeable time under zsh are sluggish under fish.
I switched to fish for about six months, and recently went back to zsh when I realised that the three things I really liked about it (abbreviations, syntax highlighting and autosuggestions) were all doable in zsh with extensions [0]. (It feels pretty slow, even slower than say OMZ, but that's not such a big deal for me.)
I switched to Fish a couple years ago, and I love it too — the only thing I ever find myself missing from bash is `!!`. The (new) Alt+S keybind to prepend `sudo` to a command covers many of these use cases, but not all of them. For example, sometimes I want to re-run the last two commands; in Bash I could do `<up><up> && !!` but there doesn’t seem to be a good way to do this in Fish without having to re-type one of the commands.
Alt+Up is great for recalling individual arguments, but am I missing something or is there no way to recall a whole previous line in-place, without overwriting what’s currently on the command line?
Up-up-Ctrl+K to kill the line; down to go down one in the history; end to go to end of line; `; and` or ` &&` to join the commands; ctrl+Y to yank the line from the killring. [0]
I tried out zsh and just could not get used to how different tab completion was vs bash, so went back to bash quickly. Is tab completion on fish more similar to bash than zsh is?
No, fish actually has super fancy autocompletion with suggestions, which is even more complicated and visually messy than the zsh defaults. Furthermore, none of it can be disabled in fish. (This is why I don't use fish, fwiw.)
On the other hand, you can disable all these advanced features in zsh and get something a bit closer to a bash readline shell, with a few extra features. That's what I use.
I didn't find it too much of a switch — it works well and things like Option-><arrow> to complete it word by word are really handy with repetitive tasks like using the AWS CLI.
I liked fish a lot but various tooling at work just expected you to be working in something sh-like. I handled sourcing files of env vars with some utility functions but I still found myself having to drop into bash/zsh pretty often.
I feel torn because I really can empathize with making a clean break. But it also precludes a lot of people from making the shell a daily driver when your co-workers are shoving shell scripts your supposed to source into your env into github, lol.
Does fish still not have reverse search? I have a hard time taking a shell seriously if you can't perform an operation at least as good as this. Even Powershell supports reverse search!
In bash, I can type ctrl+r then type a few characters, and the last command that contains that string will get pulled up and I can hit [enter] to run it.
Not sure if fish has this or not, but I use reverse search in bash exhaustively.
I find it to be more polished and especially like the way it works with fragments (e.g. if you start typing "git log" and hit Option/Alt-ꜛ it will find commands prefixed with that whereas bash Control-R requires you to hit Control-R first and then type that, which isn't as handy when you realize you want to search halfway through).
That separate mode is incremental search and it's a big advantage. It lets you keep extending the search string until it matches the command you're looking for.
fish is fantastic. abbreviations are very nice, the fish_config gui is helpful if you actually need to configure something, and there's just a lot of utility things like fish_add_path. It's just very pleasant to use.
For scripting, I’d already set a personal policy that anything complicated use Python. For me there’s a fairly big difference between interactive shell sessions and scripts which solidly shifts the balance to a full language with a rich standard library and robust error handling. My scripts were edited using shfmt and shellcheck interactively anyway, so switching to Python wasn’t an increase in terms of tool support and the richer language means that I’m writing less code to get more functionality and especially error handling.