I know I might go to posix hell for this but I just use fish shell and I don't have to worry about all that nonsense. It just Auto completes nicely. I kind of take it for granted.
It is mindboggling to me that so many people use bash or zsh and not the user-friendlier alternatives like fish, nushell and other. The arguments I hear against switching from the default usually fall into two categories:
(online) I need to use legacy systems where only (ba)sh is available and I'm not root, it's just easier to always use this, I'm used to it. Fair enough. I do ssh fairly often into my institution computing cluster and it has bash. I downloaded the fish binary (or built it maybe?), added a line in .bashrc and boom, fish there too. I understand this is not always an option.
(coworkers) I am scared of anything CLI, I don't really understand what bash is, and I just chatgpt my way through it all, I am not interested in digging deeper into this. Pretty sad, but I mean, you do you, it's fine, I know I am the weirdo who actually enjoy learning about this stuff.
I tried fish and sure it's obviously nicer than bash. I bounced off it because the syntaxes for things like for loops and variable assignments are quite different from sh and it was fighting my muscle memory.
Third category: shell is the right tool for some programs and bash is more than just the sufficient almost-always-installed default, it's pleasant to work with once familiar!
set -euo pipefail and shellcheck are great at helping the uninitiated aim footguns.
Aside, I don't think bash gets enough credit for string based near-meta programming. I think it's stronger than tcl w.r.t argument parsing.
dryrun() { ${DRYRUN:+echo} "$@"; }
# runs "long_process" w/specified arguments; DRYRUN is undefined
dryrun long process "extra complicated" arguments
# shows command and arguments instead of running it
DRYRUN=1
dryrun long_process "extra complicated" arguments
Although I can’t technically speak for @wwfn, I don’t think it’s Stockholm syndrome. I know how to write a loop or an if statement in POSIX shell because I learned it decades ago. A new shell doesn’t give me a clear benefit but it does force me to learn and remember a new syntax. But I can spend that effort better elsewhere.
I want to use fish, but I use zsh at work for one simple reason: absolutely none of our f*cking machine configuration tools support fish. I could rewrite the machine generated config to work for fish, but man there's so much and it regularly changes. I already do that for jj and it's a pain.
Using installed programs is often less friction than trying to get something approved for install. If you change jobs or contracts, your preferred program might not be allowed. I've also used bash for so long now that anything else would feel weird at this point anyways.
Many things I do have sufficient complexity to be rerun many times and do non-trivial logic. I'll just write the tool in python for easier maintenance, and avoid needing a custom shell since python is almost ubiquitous already.
It's also not that hard to make zsh behave like fish using zsh-autosuggestions and some other modifications. This is the solution I settled on after working on some projects where sourcing bash code was required to do anything.
(me) all the most effective developers I know just use bash, and anybody I see using fish seems to spend more time fumbling than us poor benighted bash users
Weirdo trying to learn, is your local .bashrc read to install things on a remote while sshing? Or how do you do it? Would you have to set this up on every machine?
It also has keybinds that match much of the aforementioned functionality. Like alt+e to edit the command line in your editor and alt+up to use the last argument.
The bash manual mentions a M-^ readline command which I guess does something like what you are thinking of. Dunno how old this feature is: I think if it had been available in the 1990s I might not have decided history expansion is utter poison. OTOH there’s still a high chance of a nasty surprise if I don’t spot the ! or forget to press M-^ to check what happens…
history-expand-line (M-^)
Perform history expansion on the current line. See HISTORY EXPANSION below for a description of history expansion.
zsh handles tab-expansion for event designators to some degree…
$ zsh --version
zsh 5.9 (arm64-apple-darwin25.0)
$ ls ~/some/path
ls: /some/path: No such file or directory
$ mkdir !:$ #<tab> will expand to ~/some/path in the readline
$ !z #<tab> again will expand to the most recent closest-match, i.e. 'zsh --version'
I like ctrl+o a lot! If you're rotating between two or three commands, like vim some.config followed by run-something, there's a chance you only need to type and run these commands once, Alt-2 or 3 Up and ctrl+o forever. It rotates back to the first of the bunch after you ctrl+o the last command of your history.
This breaks if you need to change or run a different command somewhere in between, forcing you to bring back each of the previous commands you want to rotate between to the bottom of your history to set-up ctrl+o again, so the benefits are questionable, but I enjoy it.
Honestly, being able to quickly find and edit a previous line feels so much better than this for most cases, especially for commands that can be destructive - which is a large fraction of shell commands.
As an example, I recently got bitten by the gotchas below:
(If you don't see the problem and you use find, you should find out).
These kind of problems would be a lot worse if I was doing !find to pull in the last invocation. If I actually edit the line, there is a much greater chance I'll see that it isn't what I intended.
In terms of quickly finding and editing previous commands, I currently use atuin but there are other good options, including just Ctrl-R or up and down arrows.
The good news is that in zsh, as noted in the other comments, you can use <tab> to expand so that you see what you are executing before you hit enter.
This is incredible. Thank you, especially for the short summary in the "Yell responsibly" section. I will keep referring to this and maybe eventually get a muscle memory for the different ways event designators can be used.
I wish I could disable this explanation mark magic. The only time I ever encountered it is by accident, when having a ! in a commit message. (Yeah I should use '...' rather than "..." but muscle memory is hard to change...) And when that happens you can't even arrow-up and fix the invocation since an incorrectly used ! makes the shell reject the command so early it never becomes part of the history... very annoying.
Yeah I decided history expansion is a massive footgun because it’s so hard to work out what it will do even when using it deliberately, or what went wrong when it happens unintentionally. I have had set +o histexpand in my .bashrc for nearly 30 years. Instead I sometimes use M-. to insert the final argument from the previous command, but more often mouse copy and paste.
(History expansion is a csh feature that bash copied, and it made me think that it wasn’t just csh programming that should be considered harmful!)
++, I use M-. a lot myself. I've been told M-. is not straightforward on macOS, so maybe that's why it's not so popular, but it's so handy.
edit: never learned history expansion myself- I want to say intentionally because I think it's harmful... but mostly because I'm lazy and it doesn't click with me.
I used to use these, but got burnt too many times from not seeing the expansion ahead of time. Now I just use fish, and completion from history is automatic and 100000x easier and less error-prone.
symgryph | 23 hours ago
I know I might go to posix hell for this but I just use fish shell and I don't have to worry about all that nonsense. It just Auto completes nicely. I kind of take it for granted.
nicoco | 17 hours ago
It is mindboggling to me that so many people use bash or zsh and not the user-friendlier alternatives like fish, nushell and other. The arguments I hear against switching from the default usually fall into two categories:
.bashrcand boom, fish there too. I understand this is not always an option.0x2ba22e11 | 11 hours ago
I tried fish and sure it's obviously nicer than bash. I bounced off it because the syntaxes for things like for loops and variable assignments are quite different from sh and it was fighting my muscle memory.
wwfn | 14 hours ago
Third category: shell is the right tool for some programs and bash is more than just the sufficient almost-always-installed default, it's pleasant to work with once familiar!
set -euo pipefailandshellcheckare great at helping the uninitiated aim footguns.Aside, I don't think bash gets enough credit for string based near-meta programming. I think it's stronger than tcl w.r.t argument parsing.
Halkcyon | 11 hours ago
Stockholm syndrome detected. Also funny thing, you made typos between your two snippets.
gnyeki | 11 hours ago
Although I can’t technically speak for @wwfn, I don’t think it’s Stockholm syndrome. I know how to write a loop or an if statement in POSIX shell because I learned it decades ago. A new shell doesn’t give me a clear benefit but it does force me to learn and remember a new syntax. But I can spend that effort better elsewhere.
elihunter173 | 13 hours ago
I want to use fish, but I use zsh at work for one simple reason: absolutely none of our f*cking machine configuration tools support fish. I could rewrite the machine generated config to work for fish, but man there's so much and it regularly changes. I already do that for jj and it's a pain.
0x2ba22e11 | 11 hours ago
Is there not a supported way to source env files written for bash in fish?
I think I used to do roughly
exec bash -c "source env && exec fish"quasi_qua_quasi | 2 hours ago
There's babelfish and bass; one does the rough equivalent of that, the other does static translation (can't remember which).
pyj | 13 hours ago
danlamanna | 13 hours ago
It's also not that hard to make zsh behave like fish using zsh-autosuggestions and some other modifications. This is the solution I settled on after working on some projects where sourcing bash code was required to do anything.
jfloren | 12 hours ago
(me) all the most effective developers I know just use bash, and anybody I see using fish seems to spend more time fumbling than us poor benighted bash users
junon | 10 hours ago
Fish user here, for years. I never fumble with it.
jfloren | 9 hours ago
Congratulations!
theblacklounge | 12 hours ago
Weirdo trying to learn, is your local .bashrc read to install things on a remote while sshing? Or how do you do it? Would you have to set this up on every machine?
kwas | 10 hours ago
Yea. I admin some "classic"-style enterprise apps. Sadly, RHEL doesn't come with fish packaged, and we have no EPEL there.
JulianSildenLanglo | 21 hours ago
As one of the few guys who runs powershell on linux I'm with you, it's nice to skip a lot of the bash weirdness.
viraptor | 17 hours ago
Nushell joining the non-posix gang.
Qyriad | 8 hours ago
Xonsh joining in, also with fish-like autosuggestions.
marginalia | 17 hours ago
I've modded fish to accept these substitutions. Very hard to live without them.
Levitating | 16 hours ago
It also has keybinds that match much of the aforementioned functionality. Like alt+e to edit the command line in your editor and alt+up to use the last argument.
pbsds | 19 hours ago
I don't like using these: they litter your history with history-dependent commands
leela | 17 hours ago
At least in zsh, the history only stores the expanded versions:
saturnyx | 18 hours ago
Agreed. Most of the time I just use zsh autocomplete.
JulianSildenLanglo | 19 hours ago
Would it be better if the values were expanded in the history, thus no longer being history-dependent?
pbsds | 18 hours ago
My experience with ctrl-alt-e in bash tells me that it would also break escaping, although this is something that could be fixed.
quad | 17 hours ago
The problem with
!is that— AFAIK— you can't see the expansion before running it.fanf | 12 hours ago
The bash manual mentions a M-^ readline command which I guess does something like what you are thinking of. Dunno how old this feature is: I think if it had been available in the 1990s I might not have decided history expansion is utter poison. OTOH there’s still a high chance of a nasty surprise if I don’t spot the ! or forget to press M-^ to check what happens…
banna | a day ago
zshhandles tab-expansion for event designators to some degree…koala | 19 hours ago
It is a hack, but I recently discovered ctrl+o in Bash (or I guess most readline stuff).
After you recall a command from history (with ctrl+r, for example), ctrl+o executes it and moves to the next command in the history.
diogotito | 17 hours ago
I like ctrl+o a lot! If you're rotating between two or three commands, like
vim some.configfollowed byrun-something, there's a chance you only need to type and run these commands once, Alt-2 or 3 Up and ctrl+o forever. It rotates back to the first of the bunch after you ctrl+o the last command of your history.This breaks if you need to change or run a different command somewhere in between, forcing you to bring back each of the previous commands you want to rotate between to the bottom of your history to set-up ctrl+o again, so the benefits are questionable, but I enjoy it.
spookylukey | 23 hours ago
Honestly, being able to quickly find and edit a previous line feels so much better than this for most cases, especially for commands that can be destructive - which is a large fraction of shell commands.
As an example, I recently got bitten by the gotchas below:
(If you don't see the problem and you use
find, you should find out).These kind of problems would be a lot worse if I was doing
!findto pull in the last invocation. If I actually edit the line, there is a much greater chance I'll see that it isn't what I intended.In terms of quickly finding and editing previous commands, I currently use atuin but there are other good options, including just
Ctrl-Ror up and down arrows.The good news is that in
zsh, as noted in the other comments, you can use<tab>to expand so that you see what you are executing before you hit enter.lake | a day ago
This is incredible. Thank you, especially for the short summary in the "Yell responsibly" section. I will keep referring to this and maybe eventually get a muscle memory for the different ways event designators can be used.
ralfj | 20 hours ago
I wish I could disable this explanation mark magic. The only time I ever encountered it is by accident, when having a
!in a commit message. (Yeah I should use'...'rather than"..."but muscle memory is hard to change...) And when that happens you can't even arrow-up and fix the invocation since an incorrectly used!makes the shell reject the command so early it never becomes part of the history... very annoying.tonyg | 19 hours ago
Indeed you can! The post has a section titled "! just gets in the way, how do I turn this off?".
ralfj | 19 hours ago
Ah I should have read all the way to the end. :) Thanks!
fanf | 19 hours ago
Yeah I decided history expansion is a massive footgun because it’s so hard to work out what it will do even when using it deliberately, or what went wrong when it happens unintentionally. I have had
set +o histexpandin my .bashrc for nearly 30 years. Instead I sometimes use M-. to insert the final argument from the previous command, but more often mouse copy and paste.(History expansion is a csh feature that bash copied, and it made me think that it wasn’t just csh programming that should be considered harmful!)
koala | 18 hours ago
++, I use M-. a lot myself. I've been told M-. is not straightforward on macOS, so maybe that's why it's not so popular, but it's so handy.
edit: never learned history expansion myself- I want to say intentionally because I think it's harmful... but mostly because I'm lazy and it doesn't click with me.
worr | 12 hours ago
I used to use these, but got burnt too many times from not seeing the expansion ahead of time. Now I just use fish, and completion from history is automatic and 100000x easier and less error-prone.
mro | 21 hours ago
a gem, thanks!