Fish – A friendly interactive shell
github.com
github.com
zsh felt like it fought me every step of trying to configure it to be halfway decent. And a lot of zsh scripts out there aren’t exactly bulletproof. It shouldn’t matter, except for the fact that if you use the shell with any regularity you will inevitably bump into edge cases that weren’t handled well by a mishmash of user scripts whose provenance is mostly copy/paste with little understanding of why it is all needed.
Here’s my fish dotfiles:
https://github.com/mattgreen/dotfiles/tree/master/fish
It has a submodule based plugin system (loader in conf.d), an async git prompt, my aliases, and a few env vars set. It needs no maintenance because fish provides almost everything already.
The only thing I want from fish at this point is to make async prompts first-class. They are best handled by the shell, not user code, due to the state tracking.
your commands to help not working in fish? that is rather surprising. i would think that fish users should be aware that shell specific things in fish are different, so they should have been able to cope and translate as needed.
and it's not what you can do in fish that makes it special. it's how you do it. the syntax coloring, the completions, the syntax itself...
the key thing for me is how it handles searching history and autosuggestions. i just learned how to somehow recreate this setup in zsh, and i am very curious if bash can do that too: https://news.ycombinator.com/item?id=37274207
btw i believe bash got a lot better at command completion after fish popularized the idea.
oh-my-zsh is a lifesaver here. I have a very small dotfile that delegates most of the config to omz, which I have in a bare repo that I clone onto each new machine. It took maybe an hour a few years ago to settle on the plugins I wanted and put the repo together, and each new machine now takes 5-10 minutes to get up and running.
Really feels like OMZ doesn’t know its place when it is configured by default to do that. Tools should be quiet and keep to themselves.
For example, compare this in zsh:
``` case $input in "foo") echo "Input is foo" ;; "bar") echo "Input is bar" ;; ) echo "Input does not match any case" ;; esac ```
with this in Fish shell:
``` switch $input case "foo" echo "Input is foo" case "bar" echo "Input is bar" case "" echo "Input does not match any case" end ```
And it was a really small example. Fish can do much more with "simpler" and not arcane syntax.
Fish also has `string` to work with string much much easier [0].
You miss `oh-my-zsh`? Fish has `oh-my-zsh` [1].
You miss integration with `fzf`? Fish also has it.
One caveat is Fish is not POSIX-compatible. But I don't find it a compelling reason to not using it.
Personally, I use Fish as my main shell. I do use zsh/Bash in my company though since they're more ubiquitous.
Most of the remainder of my zsh config are prompt utilities and other things I've added over the years. I don't use or really need any plugins, though if I needed, last time I tried zplug it was really easy to set up and use.
[1]: https://github.com/mid-kid/config/blob/master/shell/.zshrc#L...
If you're just reading instructions online, translating between examples given in bash and fish is trivial. I see people talk about that being an issue for them and I don't understand it; it's literally never given me trouble.
I still use bash in CI, and for Linux scripts that I distribute to others. For macOS automation, I use zsh since it's the default nowadays.
My fish scripting is mostly for personal use and occasionally little wrappers on servers that I own. Sometimes I'll share a bit of fish code with other fish users at work, too.
Fail-on-error would be nice and help make the case for using fish in more places. At organizations where no one really knows bash that well anyway, fish would actually be nicer for scripting than bash if it had just a few more goodies like that imo.
Can also do cool stuff like search for a file to open in $EDITOR and searching git log
Longer scripts get the Python treatment or a Go application.
You can run bash scripts from fish.
For sourcing, often it makes sense to use direnv to automatically load variables. -- For "just source this one time", in the worst case you can run a bash shell, source that, then run a fish shell.
There are some optional bits for more niche things (like laptop battery) you can enable with a config file, but it’s not necessary.
Doable with configuration and either lots of work or plugins in other shells but the combo of fish + starship.rs offers a lot for little setup.
The other day an AWS command was rejected with a generic 403 error and at a glance I realized I'd forgotten to switch the default profile—without starship I suspect I would have spent a while troubleshooting my SSO login instead of just switching profiles.
What's Happening with Fish Releases? - https://news.ycombinator.com/item?id=36875727 - July 2023 (1 comment)
My shell setup with Fish and Tmux (2021) - https://news.ycombinator.com/item?id=35672358 - April 2023 (80 comments)
Ask HN: Are alternative (oil, nu, etc.) shells usable as daily drivers? - https://news.ycombinator.com/item?id=34722208 - Feb 2023 (141 comments)
Rewrite it in Rust - https://news.ycombinator.com/item?id=34588340 - Jan 2023 (464 comments)
Fish 3.6.0 - https://news.ycombinator.com/item?id=34298157 - Jan 2023 (23 comments)
Fish Shell 3.5.0 - https://news.ycombinator.com/item?id=31768405 - June 2022 (71 comments)
Fish 3.4.0 - https://news.ycombinator.com/item?id=30734072 - March 2022 (90 comments)
Fish 3.4.0 Released - https://news.ycombinator.com/item?id=30660587 - March 2022 (21 comments)
Ask HN: Oil or Fish Shell? - https://news.ycombinator.com/item?id=30154652 - Jan 2022 (2 comments)
The fish shell is amazing - https://news.ycombinator.com/item?id=29341390 - Nov 2021 (290 comments)
Nsh: A fish/bash-like Posix shell in Rust - https://news.ycombinator.com/item?id=28967257 - Oct 2021 (50 comments)
Fish Shell 3.3 - https://news.ycombinator.com/item?id=27663637 - June 2021 (1 comment)
Fish shell - https://news.ycombinator.com/item?id=27180420 - May 2021 (118 comments)
Fish Shell 3.2 - https://news.ycombinator.com/item?id=26302678 - March 2021 (128 comments)
Fish is not operational on a VT220 terminal (2015) - https://news.ycombinator.com/item?id=25526237 - Dec 2020 (113 comments)
New Features in the Fish Shell - https://news.ycombinator.com/item?id=24631138 - Sept 2020 (138 comments)
Dolphins learn from their peers to use empty shells to catch fish - https://news.ycombinator.com/item?id=23660910 - June 2020 (8 comments)
Fish Shell 3.1.0 - https://news.ycombinator.com/item?id=22314671 - Feb 2020 (1 comment)
Fish: A command line shell for the 90s - https://news.ycombinator.com/item?id=21361696 - Oct 2019 (83 comments)
Show HN: Fisher 3.0 – the package manager for the fish-shell - https://news.ycombinator.com/item?id=18920972 - Jan 2019 (1 comment)
Fish shell 3.0 - https://news.ycombinator.com/item?id=18776765 - Dec 2018 (220 comments)
Fish: A user-friendly command line shell for macOS, Linux, etc - https://news.ycombinator.com/item?id=15910897 - Dec 2017 (204 comments)
Fish (Shell) for a Week - https://news.ycombinator.com/item?id=14422672 - May 2017 (2 comments)
Why I'm Hooked on Fish Shell (and How to Set It Up Right) - https://news.ycombinator.com/item?id=14417046 - May 2017 (1 comment)
The fish shell is awesome - https://news.ycombinator.com/item?id=14179081 - April 2017 (7 comments)
Fish Shell Design Principles - https://news.ycombinator.com/item?id=11102941 - Feb 2016 (71 comments)
Fish shell 2.2 - https://news.ycombinator.com/item?id=9873090 - July 2015 (70 comments)
Fish shell - https://news.ycombinator.com/item?id=9566441 - May 2015 (182 comments)
FISH Shell: A dynamic shell - https://news.ycombinator.com/item?id=8783150 - Dec 2014 (3 comments)
Fish shell 2.1 - https://news.ycombinator.com/item?id=6626635 - Oct 2013 (151 comments)
fish shell - https://news.ycombinator.com/item?id=6224524 - Aug 2013 (75 comments)
Fish shell 2.0 - https://news.ycombinator.com/item?id=5723235 - May 2013 (175 comments)
Fish 2.0 shell beta - https://news.ycombinator.com/item?id=5567639 - April 2013 (56 comments)
Fish: Finally, a command line shell for the 90s - https://news.ycombinator.com/item?id=4073162 - June 2012 (146 comments)
Fish sucks (but your shell sucks more) - https://news.ycombinator.com/item?id=2031110 - Dec 2010 (60 comments)
Fish - The friendly interactive shell - https://news.ycombinator.com/item?id=820677 - Sept 2009 (16 comments)
Fish Shell: A User-Friendly Shell or Like a Heavily Customized zsh - https://news.ycombinator.com/item?id=811113 - Sept 2009 (8 comments)
Fish is noticeably better than bash out of the box, with absolutely no customization. Magic autocomplete is life-changing.
I add starship.rs to it and fzf integration but that's it.
A shell theme
Fish conveniently provides history based autocomplete so I don't need to setup some plugin for that.
i use fish, zsh and now also elvish. the main thing for me is autosuggestion and getting history with uparrow. zsh has a module for autosuggestion. elvish has uparrow history (but only matches at the start). so fish still wins over the others. elvish comes second because typing a command and hitting uparrow once is almost like autosuggestion. but i could not find a way to get that in zsh. i also prefer the way fish and elvish handle blocks over the traditional sh-shell style.
¹ https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#...
² https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#...
prompt> some-word[up key]
will search for some-word in the history.this feature helps because in 90% of cases i can type some-word and have autosuggestion show me the right command, so i just need to hit right arrow and enter to run it. but, if the suggestion is wrong, i can instead hit up arrow and search for a another history entry.
in zsh currently, i can get autosuggestions, but if the suggestions is wrong, i have to type ctrl-A and retype some-word in order to search the history. i want to be able to do that without retyping, the way it is done in fish.
elvish only has the uparrop feature which gets me there halfway, i can type something, and then hit uparrow to find a matching command. in most cases the first hit is the one i want, but if not i keep searching.
i prefer to also have the autosuggestion because often i don't remember that i have a similar command already, and the autosuggestion provides that hint.
together these are the two most useful features that i want from a shell.
If you also need to mimic the fish behaviour of C-r mid-command because your suggestion wasn't correct, then that does need a custom function to bind to C-r as you have to provide ${L,}BUFFER to populate the search pattern like fish does.
If anybody who needs this wants to give ZSH a go, I recommend starting with only a few settings and only add new ones if you understand what they do. Same goes for plugins. I do not recommend starting with oh-my-zsh at all - which IMO is a glorified alias database anyway.
It will just be hurtful to need to keep switching between different scripting languages; I will never be able to do so comfortably and with muscle memory.
Context: I used bash for about 5 years, zsh for the next 10 before switching to fish.
I honestly think you might misunderstand why a lot of people use fish. I know bash “pretty well”—I’ve written and maintained my fair share of complex bash scripts, and remain pretty comfortable opening a shell in a running docker container or EC2 instance to debug various nonsense. What makes fish attractive to me for use as my login shell is the creature comforts which are very nice to have on my regular workstation, but aren’t really necessary when mucking around in some random environment: syntax highlighting, tab completion/autosuggestion/history search which “just works”, somewhat nicer syntax for writing simple scripts for personal use, saner word splitting, and so on. I guess I can’t really speak for other fish users and am perhaps biased by learned bash first a couple decades ago, but I don’t personally find it especially burdensome to have to remember multiple shell syntaxes/semantics (I already use half a dozen in my day to day work; what’s one more?), and I view it as a reasonable price to pay for a UI I find much more pleasant on whole.
Like for instance bulk renaming of files from the prompt, there is no way I could do that quickly in fish's language and having to switch to bash to do that would be unnecessarily slow. Might as well stick to bash.
What I mean to say is that fish would hurt my productivity rather than boost it.
With the customizations I have in my shell, my bash startup times were creeping into the multi-second territory, but I got a better UX and faster shell startup without having to learn new syntax for simple shell loops.
personally I almost never put anything that complicated into my shell, once it gets to that point it goes into a script.
You can't make a sane shell scripting language without dropping POSIX compatibility.
I mean, I really doubt that the `export` command is terrible somehow. In fish, you have to use `set -Ux` which is just ridiculous and petty of them, the world doesn't revolve around the fish developers and this just breaks many files that you try to source from fish; for example, Python venv activation scripts.
As for why I don't think that's their goal, just look at https://fishshell.com/ not one of the listed features requires them to drop POSIX compatibility entirely. I just want something like fish shell that isn't too radical and tries to be as POSIX compatible as possible while adding their improvements, out of the box (so ZSH doesn't count).
“Sensible scripting” is right there.
I tried a lot of shells and that's what I ended up with.
If you often share shell code with others it may be to big of a leap for you.
The one hiccup is the syntax; it's different from ZSH, so you have to update your existing config. ChatGPT has been pretty useful for that, though.
Now that && works, I don't have any big conplaints about it anymore.
I don't really care, but...ha! Just wondering.
I guess fish is like, you feel like you're living in the future, but also like "this is a past that should have already been available" then you find out it has been available, for 18 years, and you wonder, "Was it me that was living in the past?"
It's the kind of thing I'm scared to get burnt on, when it fails, because I already like it. I don't want it to disappoint me, because I just feel like: "new fangled, it's always gonna break." But then, it has been around for 18 years...so...hmm.
So, I don't know. It looks pretty! I like to use it. But bash is, feels, more stable.
Worth exploring! I'm not gonna config-it, as much as possible, but for interactive shell? I think it's good! :)
Yes I know I can just install fzf and the fzf/fish integrations, I've done so. But my understanding is that the whole point of fish is that it comes well configured by default. fzf is a quantum leap forward for shell UX, this sort of functionality should be built in by default.
> `Alt` + `S` Prepends sudo to the current commandline. If the commandline is empty, prepend sudo to the last commandline.
https://fishshell.com/docs/current/interactive.html#shared-b...
function last_history_item; echo $history[1]; end
abbr -a !! --position anywhere --function last_history_item
source: https://fishshell.com/docs/current/relnotes.html#id1This post has prompted me to give fish another go, so I looked into it again.
It looks like the abbreviation system can now (since March this year) help you with !! (https://fishshell.com/docs/current/cmds/abbr.html even gives it as an example)
I also use `!$` (`vim script.py` and then `python !$` or `git commit !$`) but the parser rejects `!$` before it can be rewritten by the abbreviation system. [edit: https://superuser.com/a/1762626 points out that if you add a space before hitting enter then it works fine, so I'm guessing it's just a bug, and I should go with d below]
Options seem to include:
a) make a !$ replacement that is not illegal and change your muscle memory `\$` or `!\$` or `!@` or `!%`
b) use a keybinding for !$ as suggested in https://github.com/fish-shell/fish-shell/issues/288
c) some combination of a and b (e.g. make a `!\$` abbreviation and then make a binding so that if you type !$ it replaces it with `!\$` so that it gets past the parser without expliding)
d) patch the parser to allow !$ as a special case if there is an abbr for it.
I just tried b but it's pretty jarring. I think I might go with c instead.
I'm actually feeling quite positive about this now.
There is something I've been wanting to add to bash since forever, which is something to help me cd into a repo that I just cloned (e.g. `git clone https://github.com/fish-shell/fish-shell` then `cd !/` could expand to `cd fish-shell`).
Exciting times.
[1] https://xon.sh/
I need a good shell for when I'm forced to work on Windows machines (and when WSL isn't available). Git Bash (and even plain MSYS2 bash) performance is so unbelievably awful every single time, and the fixes can be hard to determine and a giant pain in the ass, and sometimes impossible if they are due to windows defender or whatever antivirus junk the corporate environment has installed.
However, Python runs fine, so maybe Xonsh would run fine?
I realize I should probably just use PowerShell but I strongly dislike the syntax and would have to write function wrappers for basically every command I setup. I'm sure most here can relate to that.
Not sure what you mean - can you give an example?
There will be a $HOME environment variable.
I never use WSL, Git Bash or Cygwin stuff. Just the normal command prompt with xonsh running on top of it. So I can't speak to performance issues. If you could give examples, I may be able to address them.
Definitely should not have issues due to antivirus. It will be a program running continuously.
> I realize I should probably just use PowerShell
Powershell is probably the best, but I didn't want to expend energy learning it. With xonsh I can have the same config in Linux and Windows, and Python is always a plus.
if someone is saying they can’t run a bash script when they’re invoking bash from fish the most likely thing is they’re doing something wrong.
Edited to add:
There's a program out there for translating bash to fish called babelfish. It's pretty good but not complete, so it won't work for all scripts. Worth checking out, though:
The specific issue was to do with a script we had to run to gain ssh access to an internal network. I can't post the script, but it was related to openssh.
Even logging into bash and running the script didn't work, I had to remove all fish binaries, symlinks, etc and set my shell to bash/zsh. Maybe it was an issue with how I installed fish, but yeah like I said, very strange, but I can vouch that I've encountered a similar issue before
It's a real shame because I think the fish syntax alone is worth the switch, for my personal machine(s) I use it.
Edit: this was years ago now, about 4 years, maybe the issue doesn't exist anymore but it caused me a bit of grief at the time (beacuse I didn't know it was related to using fish!), I've not since tried it again. I might try again and get back to this thread
although such a name conflict should not have happened (and i can't think of which command such a script might have used that would also be a fish command), and the global path thing was also fixed soonish. but this is all a faint memory, so i am not sure i remember any of that right.
- cd
- source, .
- eval
- string
- and
- or
- builtin
- command
and many others.Not sure what the point of distinguishing between fish builtins and fish functions is; whether a builtin is shipped as a function distributed with fish or a reserved word in the fish evaluator seems like an implementation detail.
Builtin commands should only be created when it cannot be avoided. echo, kill, printf and time are among the commands that fish does not implement internally since they can be provided as external commands. Several other commands that are commonly implemented as builtins and can not be implemented as external commands, including type, vared, pushd and popd are implemented as shellscript functions in fish.
if i remember correctly, this led to some useful commands that are builtin elsewhere to be external binaries shipped with fish. but since those where not actually tied to the fish shell they could run without it, and if they ended up on the global path be accessible from other shells.
the relevant text on the website has been changed, but it is referenced here:
https://github.com/fish-shell/fish-shell/issues/612
the discussion also points out that this has changed over time
If you wanna add a builtin like that to your own distribution of fish, you could do it cleanly by keeping those binaries in /usr/lib or /usr/libexec and then wrapping them in a fish function that ships in fish's install prefix. (This is basically how fish's Python scripts for generating completions from manpages are shipped today.)
right, i don't remember the details, but when it happened it was probably fixed quickly. could even have been a packaging error in a distribution.
Most likely a command in your script (or one source-d into it) makes an assumption about your $SHELL or login shell that is true for bash/zsh but not for fish.
Merely adding a shebang won't fix such a script.
ssh-agent $SHELL ...
or exec $SHELL ...
or eval "$SHELL ..."
or similar. :)If it ends up passing a script file or arguments to $SHELL and you wanna fix it without rewriting it, just add some conditionals where $SHELL is used and use the equivalent flags or syntax when $SHELL indicates fish.
Alternatively, if you just wanna work around it, just launch that script like
env SHELL=(which zsh) whatever-script.share you sure you're running `./scriptname` or bash scriptname`
eval "$SHELL ..."
because (at least sometimes?) $SHELL is set by your login program and not your shell, and dropping to an alternative shell by just typing zsh
and hitting enter or whatever won't reset $SHELL.You mention elsewhere that this had to do with a special OpenSSH setup, which also fits.
One of the things you can do with ssh-agent is use it to launch a child process with a dedicated SSH agent that (only) has certain keys on it, and which exits once its child exits. This is sometimes handy for deployment scripts or doing git checkouts or whatever because you can ensure you don't get locked out for too many auth attempts because the user just has too many extraneous keys on the agent associated with their normal user session.
If you were leveraging this feature for interactive shells, you might be tempted to use $SHELL to decide what executable to have ssh-agent launch, so that you could (for example) accommodate both bash and zsh users and let them launch a shell with a special SSH agent but which honors their usual preferences by loading their usual shell and reading their usual zshenv or bashrc or whatever it is.
You mention as well that you had to actually uninstall fish to get things to work again, and also that you were on macOS. I can't be certain about the cause here, but one obvious thing occurs to me:
macOS doesn't handle environment setup like any 'normal' (Linux or *BSD) Unix. On normal Unices, your login shell actually also launches your graphical user session, so to configure a session-wide environment variable, you just set it in your shell startup somewhere. On macOS, login shells are actually only used in SSH sessions and terminal emulators. If you want to set environment variables for apps that are not launched from terminal emulators, you have to use hacks (like Doom Emacs' env file, for example) or configure them for the whole user session via a LaunchAgent.
In the case of $SHELL, that environment variable is used by editors to determine what shell to use when you run external commands. Without it, if you are a fish user and you launch MacVim or Emacs (or, presumably, VSCode or anything else) from your dock, when you go to launch external commands, they will run not in fish but some other shell (probably zsh or bash). If your install method for fish tried to handle this for you (pkgsrc and Nix don't, but the .pkg from the developers or Homebrew might, idk), or if you dropped to bash only from terminal emulator windows that were already running fish instead of opening a new session for bash, you may have wound up in a bash session where $SHELL was still set to fish. That's the only reason I can think of for why you might have had to actually uninstall fish to get this ill-behaved script to work right.
Anyway:
1. As a user, pay attention to what $SHELL is.
2. As a script author, never use `eval "$SHELL ..." or equivalent`.
That's no longer true for Linux, at least. GDM launches stuff via systemd user sessions, which do not source your profile.
I'm not sure how I feel about it. The old way feels 'simple' to me, but the new way does mean that shell misconfiguration won't hose your GUI sessions.
I use xonsh and fortunately it has source-bash for stuff like that.
bash -c "grep word file.txt | sort -u | tail"
Scripts shouldn't be an issue as long as you either launch it with the proper shell or configure the shebang correctly after making the script executable.https://ilya-sher.org/2022/12/31/telegraph-and-the-unix-shel...
I personally just put what path modifications I need in .profile and have fish as my login shell.
When people first try out fish they want to add their global env vars and PATH configuration to ~/.config/fish/config.fish. You can absolutely do this similar to how you do it in ~/.bashrc. However, it's not idiomatic and once you get used to universal variables, you'll see that it's a lot of yak shaving you really just don't need editing config.fish.
Instead, if you want to permanently set an env var across the current session and all new sessions, run: `set -Ux MY_ENV_VAR 1` (set a universal variable, and export it to subprocesses)
That will put it into a machine-readable file ~/.config/fish/fish_variables. You can open it if you want but this way you don't need to source your rc files, open a new shell, or even open your editor.
For PATH, just run: `fish_add_path ~/my-new-bin`. It uses universal variables by default.
For some reason I avoided universal variables as much as possible for years using fish and now I think that was really silly.
export PATH="$PATH:$HOME/foobar/"Nushell is really more about command output (my understanding, I haven't used it heavily). With Nushell you can do things like parse TOML files right in your shell:
open Cargo.toml | get package.version
Nushell understands the structure of data but fish is like bash/zsh in that it just deals with streams of text. Nushell can even interact directly with sqlite databases which is very cool.I think if you're just looking for a simple shell that behaves a lot like bash/zsh, use fish. I think Nushell is more targeted to people that want to use its powerful data pipeline features—or are generally interested in a bigger departure from bash/zsh.
Personally I prefer fish, but I should start using Nushell more since it could definitely help with some tasks. I probably would just run Nushell when I need it though, I don't see myself running chsh to switch over.
Command line arguments! Remote path completion! Git!
It’s something I install basically on everything machine I can. The history based autocomplete is so useful for keeping track of and refinding those less used complex commands that you have not saved in a function yet.
BUT...
After adding the following plugins to zsh(before you chime in, it's just adding these lines,not anything configuring much. also it auto bootstraps on new install), I found out that fish is no where as good as configured zsh.
1) https://github.com/zdharma-continuum/zinit (plugin manager)
2) https://github.com/zdharma-continuum/fast-syntax-highlightin...
3) https://github.com/zdharma-continuum/history-search-multi-wo...
4) https://github.com/zsh-users/zsh-autosuggestions
5) https://github.com/zsh-users/zsh-completions
6) https://github.com/Aloxaf/fzf-tab
7) any good shell prompt generator like https://github.com/romkatv/powerlevel10k
For example, I use fzf integration for tab completion. Fish's fzf integration is nowhere as good as that of zsh's. Also, posix compat and almost bash compat of zsh is plus.
I acknowledge that zsh isn't perfect shell either and I have tried and failed few times in past to switch to fish. If you provide me compelling reason/s to switch to fish, I am all ears.
It works for me. It’s a great tool.
I tried zsh; zsh was a lot slower as I set it up. Maybe I did it wrong.
I think the case is that with either of fish or zsh you can’t lose. Both are great.