I use Zsh, it's such a tremendous improvement over Bash. Not only does it handle parameter expansion much better than any Unix-style shell, but it also has a lot more useful built-ins that can save you from having to call out to external programs to do basic tasks, like getting the absolute path of the directory that contains the current script.
It is so unbelievably feature-rich that it's almost impossible to describe all of its features and merits, but you can safely ignore most of them and the basic day-to-day experience will just be "Bash, but better".
No program is perfect, but Zsh I think is pretty damn close. That's not because it doesn't have flaws (it definitely has flaws), it's because it does such a good job at doing what it does, that all its flaws seem minor and none of them fundamentally get in the way of it doing that job.
One additional thing I'll say is: it is probably the only Unix-style shell where I would feel comfortable writing 1000+-line applications with a CLI, tests, etc. I would never do such a thing with Bash or some minimal Posix-compatible shell. It's a very sensible system scripting language, alongside Python, Perl, etc.
It may be better bash, but it's just an incremental improvement, no more. It still follows the same philosophy as bash.
On the other hand, fish shell feels like a bash rethought and optimized for interactive use. It's now my daily driver on all of my Linux and Windows machines (msys2 port). After using it only for interactive sessions, I also started to write my utilities in fish, which is a breath of fresh air compared to both bash and zsh.
The only place where I still use bash is when I need to write a script that needs to be shared with others, everything else is now fish.
> It's a very sensible system scripting language, alongside Python, Perl, etc.
I’ve always assumed that the main benefit of bash scripts is that they’re portable and universal. If you’re willing to compromise that, why not just go with python? I don’t really understand which gap zsh fills here.
I am very aligned with what you said but when it comes to shipping shell scripts (say sysops stuff, init, cron, ..) I tend to stick with the ksh/bash ecosystem mainly due to more tooling (shellcheck, shfmt, bats/bash_unit).
Most things that works in bash obviously works fine in zsh, but as the article very eloquently points out - lots of gotchas.
Would love to be able to transpile zsh or any other higher level scripting language into Bash for portability reasons alone. Is there something that converts bash, sh, or zsh into an AST?
Also useful would be a tool that tells me if I'm using a zsh construct that isn't supported in bash... or even better, how to do it in bash
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.
`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 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?
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?
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!
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.
While these are good examples of some of the fragile parts of bash, I think switching shells is the wrong approach in this case. If you find yourself dealing with binary data, floating point math, complex quoting, and so on...you probably shouldn't be scripting with a shell. Use Python, Perl, Ruby, or something else more suited to the task.
I don't agree with this. Shell syntax is uniquely flexible and powerful when it comes to composing pipelines. It often is precisely the right tool for the job.
Floating point math and "complex" quoting come up pretty often in a wide range of relatively basic tasks. Even something as simple as computing a percentage literally cannot be done in Bash without forking out to an external program. Sure, you could use a tool that does that one thing and does it well, or you could just have it in your shell and not worry about it.
The whole point about quoting is that it's hard to get right even when you're trying. It gets even harder when you are dealing with things like arrays and command substitution. You could switch to another programming language... or you could just use a shell that does these things right from the outset!
I used to say this, but Powershell has really proven to me that we need a shell that is also a good scripting language. Powershell has its faults, but it's as good as any scripting language, especially when time is a factor. And it does this without sacrificing its utility as a shell.
I haven't spent time with elvish but I think it's moving more in the right direction. Zsh and friends are better than bash but they are still too sh-like and a lot of those conventions are just always going to be problems.
There are all sorts of cases where you have a simple script that's perfectly fine, but you want to just add two fractions, or store a bit of binary data in a variable. I think those are reasonable expectations, and I don't think that "rewrite it to {Python,Ruby,...}!" is really a great solution (partly because for some simple tasks writing it in Python, Ruby, etc. is a lot more work). "If you want it, then this is not the right tool" doesn't strike me as a very strong argument for missing basic features such as this. For advanced complex features? Maybe. But not this.
I also don't think shell scripting is quite as bad as people make it out to be, or rather, one of the reasons people hate it is because they use bash or POSIX sh, and yeah, that gets problematic fast. The point of the article was to demonstrate that actually, you can solve a decent chunk of the problems and get something much less problematic without a lot of effort, while still retaining the key advantages of shell scripting.
However, even if we exclude all of scripting: you can't write your ~/.{bash,zsh}rc in Python, and it's useful for interactive usage as well.
So I read this with a great deal of interest and nodded my head say “yes, that’s true” at most of it. Despite this I just do not want to switch. Heck, first thing I do when I get a new Mac (now that ash is the default)is switch my shell to bash, install homebrew, then tmux, vim, powerline and git pull my dot files. I think maybe I am just old. Get off my lawn!
I am really working on not writing every little thing I want to script as a shell script. The other day I wanted to scan a dir of ~10k image files and move anything that was corrupt to another dir. I wrote it in bash and it worked. Then I was like, okay you should write this in python because you want to run it on 4 different OS. So I did. The code looks so much cleaner but was half as fast as the shell script. Oh well.
This is me as well - unabashedly a bash person. My reasons to not switch are not driven by the feature set offered by the various shells, but by the fact that I don't want to learn/think or be bothered with the nuances when I log in to various remote hosts, containers, and what have you. When I write shell scripts or installers, I want a single language that is reasonably likely to be present wherever it runs - and that today is bash.
We had _just_ settled the emacs vs vi argument when this starts ...
I guess debian/ Ubuntu folks will choose bash, osx people will continue using zsh and the openbsd users will keep using that vanilla sh because of the vintage feel.
On a more serious note: I can't see my commands in manjaro thanks to all the bells and whistles. For some reason that situation reminds me of spacemacs users.
> I guess debian/ Ubuntu folks will choose bash, osx people will continue using zsh and the openbsd users will keep using that vanilla sh because of the vintage feel.
I believe that using zsh means, for the vast majority of users, using just a small subset of functionality that gives a better UX when compared to Bash.
Both me and my colleagues use zsh this way, and the times I've tried to figure out more advanced functionalities, it wasn't as simple as I expected.
I've read a few points of the article, and although they make sense as improvements, they apply mostly to the scripts domain. I don't recommend writing zsh scripts, because they're subtly different from the (unfortunately) standard that is Bash, besides not being supported (at least until a short time ago) by (the necessary) Shellcheck. If one wants a better experience for scripting, they're better off with a non-shell scripting language altogether (ie. Python).
What I like best about zsh is how easy it makes normal tasks.
* dont know the full name of the folder you want to cd into? Type the 1st few characters, and press tab, zsh will let you select which one you want
* Oh, and search is case insensitive. No more spending hours banging at the tab, wondering why autocomplete isnt working, just because you typed "projects" instead of "Projects"
* history search is great-- type the 1st few lines of a command and press up, it will take you to the last time the command was used
* Oh my zsh gives you a lot of cool utilities-- instead of
I feel obligated to point out that this is mostly doable with BASH.
* I don't understand how this is different than usual tab-completion; might just be missing something. You can stick `set show-all-if-ambiguous on` in .inputrc to make it activate on 1 tab instead of 2.
I'm glad you like ZSH, for those wondering if you can do similar things in Bash (lots of non technical folks on the site these days).
* I have to admit I tend to run mlocate on my systems and grep for full file paths instead of using tab completion if I don't know what the directory path is. However if you want auto complete with a list to pick from like ZSH
bind 'set show-all-if-ambiguous on'
bind 'TAB:menu-complete'
in your inputrc will do the trick.
* echo set completion-ignore-case on | sudo tee -a /etc/inputrc
(use .bashrc instead of input if you only want to set this for your own user)
* ctrl + r type part of your command history search in bash
* alias gcam='git commit -a -m'
ZSH definitely has a lot of cool things (globbing is really nice for instance), but Bash is ubiquitous and it's good to know how to make your life easier if you are working on a server that doesn't have your fave shell installed.
Happy hacking!
When I started typing this there were no replies, I got distracted by a work slack, and came back to hit submit, and there are two basically duplicates. An excellent reminder that none of us are actually the main character so to speak.
> Click to see NSFW content. Not suitable for children under 18!
click
> For example, this can be used to show the longest element in an array:
> print ${array[(r)${(l.${#${(O@)array//?/X}[1]}..?.)}]}
> NUL bytes aren’t that uncommon, think of e.g. find -print0, xargs -0, etc. That all works grand, right up to the point you try to assign it to a variable.
Well, duh. Environment variables are NUL-terminated.
I don't understand arguments like this. "Learn something new so that you can avoid learning something old". None of the arguments on this page show me that zsh has a shallower learning curve than bash - only that zsh makes "more sense", once you've learned it, to you.
I tried switching to zsh, but I felt like out of the box it's almost the same as bash. I don't want to spend a lot of time configuring my shell, I want it to get out of my way and let me type commands.
I use fish shell mostly these days. Magic autocompletion is life-changing.
People talk about zsh being more advanced than Bash as if that's a good thing. But remember the shell is mostly useful as an interface to other programs; it's not supposed to be filled with features. Bash has way too many features, I think, for the same reason zsh does: people thought it was philosophically superior to rely on the shell having built-in features, rather than finding or developing composeable applications..
Is there a way to change the autocomplete key in zsh from tab to esc? I know it's a minor thing but it really does screw up my desire to switch. I looked around and the solution wasn't obvious or my google-fu is severally lacking.
Use what you like. I use zsh at home. At work, it is ksh, sh, or bash on the servers. I don't see that changing (except ksh going away when we replace Solaris) for a very long time, if ever.
I strive to use bash as minimally as possible anyway, I don't quite see the point of switching to zsh when I want to avoid doing things in the shell (rather than scripting or using cron jobs or similar).
Why would people want to use an alternate shell? How much do you need to be constantly sat in the shell typing out the same commands rather than either scripting them away or doing something within a text editor?
I use those functions in both scripts and my interactive shell (which is bash).
I cannot do the same with zsh as my interactive shell if scripting is supposed to stay POSIX/bourne shell. Which I want as I often do this in systemwide scripts or modify existing scripts (all plain sh). The incompatibilities do matter.
I tried zsh and have not found anything that is truly superior to bash. After some investigation, it seems that this idea of moving from bash is based more on the fact that zsh is the default for Apple, due to their dispute with GNU licensing terms.
Honestly, all these shells are too simple or at least too generic-task oriented. For me, and I guess most users, the shell is not where I live, but where I do recipes. It would be nice to have a shell+vim style shell where you edit your command plan and can see its execution results right below the commands after pressing <C-CR>:
So you could move and edit pages (or OBTF) of recipes and see their status right there. My observation is that all you do in a shell is following some patterns again and again. For example, select last three commands and hit C-CR to execute them.
[+] [-] nerdponx|4 years ago|reply
It is so unbelievably feature-rich that it's almost impossible to describe all of its features and merits, but you can safely ignore most of them and the basic day-to-day experience will just be "Bash, but better".
No program is perfect, but Zsh I think is pretty damn close. That's not because it doesn't have flaws (it definitely has flaws), it's because it does such a good job at doing what it does, that all its flaws seem minor and none of them fundamentally get in the way of it doing that job.
One additional thing I'll say is: it is probably the only Unix-style shell where I would feel comfortable writing 1000+-line applications with a CLI, tests, etc. I would never do such a thing with Bash or some minimal Posix-compatible shell. It's a very sensible system scripting language, alongside Python, Perl, etc.
[+] [-] tut-urut-utut|4 years ago|reply
On the other hand, fish shell feels like a bash rethought and optimized for interactive use. It's now my daily driver on all of my Linux and Windows machines (msys2 port). After using it only for interactive sessions, I also started to write my utilities in fish, which is a breath of fresh air compared to both bash and zsh.
The only place where I still use bash is when I need to write a script that needs to be shared with others, everything else is now fish.
[+] [-] hnarn|4 years ago|reply
I’ve always assumed that the main benefit of bash scripts is that they’re portable and universal. If you’re willing to compromise that, why not just go with python? I don’t really understand which gap zsh fills here.
[+] [-] jbergstroem|4 years ago|reply
Most things that works in bash obviously works fine in zsh, but as the article very eloquently points out - lots of gotchas.
[+] [-] chrisweekly|4 years ago|reply
[+] [-] pmarreck|4 years ago|reply
Also useful would be a tool that tells me if I'm using a zsh construct that isn't supported in bash... or even better, how to do it in bash
[+] [-] sophacles|4 years ago|reply
TIL zsh is even better than zsh!
[+] [-] merb|4 years ago|reply
[+] [-] acdha|4 years ago|reply
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.
[+] [-] kstrauser|4 years ago|reply
- 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.
[+] [-] shawndellysse|4 years ago|reply
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
[0]: https://starship.rs
[+] [-] revscat|4 years ago|reply
[+] [-] lordgrenville|4 years ago|reply
[0] https://github.com/olets/zsh-abbr
https://github.com/zsh-users/zsh-syntax-highlighting
https://github.com/zsh-users/zsh-autosuggestions
[+] [-] NobodyNada|4 years ago|reply
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?
[+] [-] indigodaddy|4 years ago|reply
[+] [-] krinchan|4 years ago|reply
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.
[+] [-] beebmam|4 years ago|reply
[+] [-] Barrin92|4 years ago|reply
[+] [-] tyingq|4 years ago|reply
[+] [-] nerdponx|4 years ago|reply
Floating point math and "complex" quoting come up pretty often in a wide range of relatively basic tasks. Even something as simple as computing a percentage literally cannot be done in Bash without forking out to an external program. Sure, you could use a tool that does that one thing and does it well, or you could just have it in your shell and not worry about it.
The whole point about quoting is that it's hard to get right even when you're trying. It gets even harder when you are dealing with things like arrays and command substitution. You could switch to another programming language... or you could just use a shell that does these things right from the outset!
[+] [-] lukeschlather|4 years ago|reply
I haven't spent time with elvish but I think it's moving more in the right direction. Zsh and friends are better than bash but they are still too sh-like and a lot of those conventions are just always going to be problems.
[+] [-] arp242|4 years ago|reply
I also don't think shell scripting is quite as bad as people make it out to be, or rather, one of the reasons people hate it is because they use bash or POSIX sh, and yeah, that gets problematic fast. The point of the article was to demonstrate that actually, you can solve a decent chunk of the problems and get something much less problematic without a lot of effort, while still retaining the key advantages of shell scripting.
However, even if we exclude all of scripting: you can't write your ~/.{bash,zsh}rc in Python, and it's useful for interactive usage as well.
[+] [-] vips7L|4 years ago|reply
[+] [-] myrandomcomment|4 years ago|reply
I am really working on not writing every little thing I want to script as a shell script. The other day I wanted to scan a dir of ~10k image files and move anything that was corrupt to another dir. I wrote it in bash and it worked. Then I was like, okay you should write this in python because you want to run it on 4 different OS. So I did. The code looks so much cleaner but was half as fast as the shell script. Oh well.
[+] [-] i2shar|4 years ago|reply
[+] [-] nickthemagicman|4 years ago|reply
[+] [-] zibzab|4 years ago|reply
I guess debian/ Ubuntu folks will choose bash, osx people will continue using zsh and the openbsd users will keep using that vanilla sh because of the vintage feel.
On a more serious note: I can't see my commands in manjaro thanks to all the bells and whistles. For some reason that situation reminds me of spacemacs users.
[+] [-] pizza234|4 years ago|reply
I believe that using zsh means, for the vast majority of users, using just a small subset of functionality that gives a better UX when compared to Bash.
Both me and my colleagues use zsh this way, and the times I've tried to figure out more advanced functionalities, it wasn't as simple as I expected.
I've read a few points of the article, and although they make sense as improvements, they apply mostly to the scripts domain. I don't recommend writing zsh scripts, because they're subtly different from the (unfortunately) standard that is Bash, besides not being supported (at least until a short time ago) by (the necessary) Shellcheck. If one wants a better experience for scripting, they're better off with a non-shell scripting language altogether (ie. Python).
[+] [-] todd8|4 years ago|reply
We did? That’s news to me!
[+] [-] Hamuko|4 years ago|reply
[+] [-] pjmlp|4 years ago|reply
[+] [-] unknown|4 years ago|reply
[deleted]
[+] [-] forgotpwd16|4 years ago|reply
[+] [-] shantnutiwari|4 years ago|reply
* dont know the full name of the folder you want to cd into? Type the 1st few characters, and press tab, zsh will let you select which one you want
* Oh, and search is case insensitive. No more spending hours banging at the tab, wondering why autocomplete isnt working, just because you typed "projects" instead of "Projects"
* history search is great-- type the 1st few lines of a command and press up, it will take you to the last time the command was used
* Oh my zsh gives you a lot of cool utilities-- instead of
git commit -a -m "message"
I can do
gcam "message"
Moving to zsh was the best decision I made.
[+] [-] yjftsjthsd-h|4 years ago|reply
* I don't understand how this is different than usual tab-completion; might just be missing something. You can stick `set show-all-if-ambiguous on` in .inputrc to make it activate on 1 tab instead of 2.
* `set completion-ignore-case on` in .inputrc
* https://askubuntu.com/questions/59846/bash-history-search-pa...
* `alias gcam='git commit -a -m'`, although I assume you're pointing out that it gives you a bunch of default aliases?
[+] [-] jdhendrickson|4 years ago|reply
* I have to admit I tend to run mlocate on my systems and grep for full file paths instead of using tab completion if I don't know what the directory path is. However if you want auto complete with a list to pick from like ZSH bind 'set show-all-if-ambiguous on' bind 'TAB:menu-complete' in your inputrc will do the trick.
* echo set completion-ignore-case on | sudo tee -a /etc/inputrc (use .bashrc instead of input if you only want to set this for your own user)
* ctrl + r type part of your command history search in bash
* alias gcam='git commit -a -m'
ZSH definitely has a lot of cool things (globbing is really nice for instance), but Bash is ubiquitous and it's good to know how to make your life easier if you are working on a server that doesn't have your fave shell installed.
Happy hacking!
When I started typing this there were no replies, I got distracted by a work slack, and came back to hit submit, and there are two basically duplicates. An excellent reminder that none of us are actually the main character so to speak.
[+] [-] wchar_t|4 years ago|reply
Got me there :^)
[+] [-] jfrunyon|4 years ago|reply
Well, duh. Environment variables are NUL-terminated.
I don't understand arguments like this. "Learn something new so that you can avoid learning something old". None of the arguments on this page show me that zsh has a shallower learning curve than bash - only that zsh makes "more sense", once you've learned it, to you.
[+] [-] assbuttbuttass|4 years ago|reply
I use fish shell mostly these days. Magic autocompletion is life-changing.
[+] [-] throwaway984393|4 years ago|reply
[+] [-] gruturo|4 years ago|reply
[+] [-] jeffrallen|4 years ago|reply
[+] [-] protomyth|4 years ago|reply
[+] [-] sigzero|4 years ago|reply
[+] [-] hd4|4 years ago|reply
[+] [-] nixpulvis|4 years ago|reply
Practically, I find myself typing `irb` before every quick arithmetic session anyway... but w/e.
[+] [-] cracauer|4 years ago|reply
I use those functions in both scripts and my interactive shell (which is bash).
I cannot do the same with zsh as my interactive shell if scripting is supposed to stay POSIX/bourne shell. Which I want as I often do this in systemwide scripts or modify existing scripts (all plain sh). The incompatibilities do matter.
[+] [-] 1vuio0pswjnm7|4 years ago|reply
where sh is NetBSD's ash, with command history
[+] [-] coliveira|4 years ago|reply
[+] [-] wruza|4 years ago|reply