Hacker News new | past | comments | ask | show | jobs | submit login
s/bash/zsh/g (arp242.net)
191 points by zoidb 3 months ago | hide | past | favorite | 237 comments

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.

Within 30 seconds of installing fish, I shouted, "How the hell do I disable these annoying suggestions?! This is worse than Clippy!" So I searched fish's docs and learned that it wasn't possible to disable the suggestions! The fish docs state, "Configurability is the root of all evil"[1]. As an Emacs user, this was the most offensive thing I've ever read, which is saying a lot because I once read a work of erotic Star Trek fan fiction about a love triangle involving Dr. Crusher, Wesley and Worf.

[1] https://fishshell.com/docs/current/design.html#configurabili...

not the link I was hoping for ;)

hahaha same

If you don't want suggestions and completions, fish is definitely not the right shell for you !

That said fish is quite configurable¹, it just have pleasant (well maybe not to you) default that you can change.

1- The ssettings are mostly listed there https://fishshell.com/docs/current/interactive.html

I have to admit, I’m not surprised the fish user would footnote with (an unclosed) ¹ on HN. :) As for me, I prefer the predictability of [1] and /bin/bash.

I use fish as my default user interactive shell but I still agree with you. I despise this philosophy of "configurability is the root of all evil" coming from fish and "every preference has a cost" coming from GNOME.

I think these projects are extremely opinionated and they will inevitably drive a lot of users away. Both fish and GNOME strive to provide "good" defaults out of the box but those defaults will never appeal to everyone. I ended up liking fish but I still despise GNOME and everything related to it.

Likewise, I don't use fish for exactly this reason. It seems extremely fine tuned to meet exactly the preferences of its developers, and eschews all further configurability. The Clippy comparison is brilliant, by the way, pretty much summarizes my exact feelings about the suggestions.

I ended up back on zsh. It's a real shame, because fish adds quite a lot in terms of improved subshell syntax and intelligent string manipulation.

Seems like someone should have been able to disable the suggestions using a plugin or something, given that IIRC fish has a plugin interface. But as far as I can tell no such thing exists.

The advantage of that, though, is that you can still benefit from the many "how do I do X in bash" posts on various fora, Stack Exchange, etc. With the more esoteric shells you have a much smaller community.

It's very infrequent that you're doing something truly novel with your interactive shell.

For scripts, you can still use bash or zsh or python or whatever you want.

> you can still use bash or zsh or python

I prefer to keep my utilities scripting and interactive shell the same. That way I don't have to keep two sets of gotchas in my head, and don't accidentally use the wrong syntax in one place or the other sometimes leading to confusing misbehaviours.

While I don't mix bash/zsh/sh/... I will consider python/node/other if I need specific things that they do well or I'm making something more than a simple tool or automation (for which bash is either underkill or just thoroughly unsuited). They are sufficiently different that I'm not going to cause myself the sort of mix-up I might with flipping between shell scripting syntaxes.

The really cool thing about shell scripts is that they are just a saved version of your interactions with the ability to edit and refine later. If you stop using the shell interactively you've just lost the main advantage of using it for scripting.

That's a very good point. I have stuck with it because I am comfortable with it, but I have been wanting to spend some quality time with these alternative shells for a while. Have you tried Elvish? I am particularly curious about that one, because it seems like the primary Fish "competitor", but seems comparatively a lot less popular.

I also use fish on basically every linux machine, desktop or server, but I've also tried elvish for a bit. My shell scripts are still mostly bash or even POSIX though, because I haven't rewritten them yet, or they're setup scripts that should work right after the install without installing an additional shell first.

I'll admit that I haven't really dug into scripting with elvish, because I quickly abandoned it again.

My main reason for leaving elvish again was the good syntax completion in fish. Yes, you can create your own completions in elvish and they'll probably be just as good, but that takes time and I ultimately decided not to bother with it.

> it's just an incremental improvement, no more. It still follows the same philosophy as bash.

It is actually the other way round, with bash being an incremental improvement over zsh. Starting from v3, bash has adopted many features that zsh had had for a long time.

Both, bash and zsh, are incremental improvements over the Korn shell.

> 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.

> why not just go with python?

I can't speak for anyone else, but for me, it's because Python is cumbersome for doing the sorts of things that are easy in a shell. You want to capture the output of a command, parse it as a tab-separated table, extract the 3rd and 4th columns, and then use them to create new users with the values supplying usernames and passwords. Can you do that easily in Python?

For all its syntactic sugar, dealing with files in python (which comprises probably 90% of what I open a shell for) still requires a fair amount of boilerplate. Which is good for a general scripting language, but makes it poor for the the kind of tasks I expect of a shell.

> Can you do that easily in Python?

I'm not arguing for Python over bash, I'm asking about the value of zsh as opposed to bash or python. So why couldn't one do what you describe in bash? The answer probably is you very well could. So why use zsh for scripting?

I've generally ported _to_ Python to see improved portability: the problem with bash is that it's not just what versions of bash you have installed (if you support RHEL, MacOS, etc. that can limit you significantly) but also all of the utilities which you call so you probably end up needing to wrap things like readlink once you need to run on more than one of GNU, BSD, busybox, etc.

Obviously you can get a fair amount done with pure POSIX but for me it's almost inevitably hit the point where you rewrite it in something else and realize that it's like 30% fewer lines of easier to understand code.

Fine, but this does nothing to address the value of zsh which is what I was asking about.

Furthermore, interactive zsh totally breaks vim-like input mode.

If you choose vim keybindings for navigation, it utterly breaks reverse-r search and the hacks you have to use to fix it do not make it an equivalent experience.

It's like the zsh folks said, "fuck those vim guys".

If I didn't have to use a Mac at work and our tooling didn't behave oddly in bash, I wouldn't use it. Bash is much more comfortable.

What are you talking about? Using reverse-r search in vi mode requires a single line config, no hacks involved.

    bindkey -v '^R' history-incremental-search-backward
I'd say that if you want vim-like bindings, zsh is a much better option. It provides Vim's text objects, surround.vim bindings, and visual selection which bash does not. Also unlike bash, you're free to create your own keybinding if none of the builtin ones suffice. I did exactly that to get an increment/decrement operator that behaves exactly like Vim's ^a/^x.

It's like the zsh folks said, "I like those vim guys".

As a vim user, zsh's vim mode is far better. You do need a few lines of config for stuff like allowing deleting back past the insert position. However, I have never found it lacking, unlike bash which sometimes lacks some motion I try to use.

I didn't mean to imply that Zsh was somehow more portable than Python. And I agree with the sibling commenter that Python is probably more portable than Bash.

I use Zsh for many of my personal scripts, but at work I would never even try to get it into one of our production containers.

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.

Yeah, zsh is great. IMHO Oh-my-zsh is the place to start, for those curious about its capabilities.

I sure love it when my shell takes seconds to load on startup.

I hear this argument from zsh purists all the time, I'm pretty sure it's made up. I've used oh-my-zsh for years with several plugins and the startup time is basically imperceptible. Maybe it's slow if you're running on raspberry pi or something.

Not a raspberry pi but a Macbook.

Yeah, I can't quite comprehend what makes the terminal experience on MacOS feel so sluggish. At first I thought I was being spoiled by Alacritty, but even Konsole feels more responsive than iTerm these days.

~/.zshrc:L1:"zmodload zsh/zprof"


Doesn't take long to track down the culprits. nvm is a huge contributor to startup lag, IME. I am not a frontend dev, and rarely have to touch Node, so I comment out anything nvm-related for daily work.

Nvm startup times are the worst. I use it to install npm versions, but then manually add those to my $PATH.

There's a --no-use flag that short-circuits all the heavy startup processing, but still gives you access to the command.

Here's a snippet from my zshrc: ``` export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use export PATH="$HOME/.nvm/versions/node/v14.17.6/bin:$PATH" ```

I agree nvm is suboptimal. I've been doing web-related things for a living since 1998 and I care about tooling. YMMV but I have a strong preference for managing node versions via `n` (installed via tiny util `n-install`).

My Node-related macOS terminal setup:

- iTerm.app

- zsh (via brew)

- n-install -> n -> node (/npm)

- yarn (via npm i -g)

- pnpm ("")

HTH! :)

For folks who use nvm and find it slow, try out Volta[1]. It’s much faster, and IMO a lot nicer to use in general.

1: https://volta.sh/

Yet another NVM alternative is FNM, which is very fast. I'm surprised there are so many others!

I just use nodenv via anyenv. All the ${X}vm utilities have massive startup lag for some reason.

It is easy to go overboard with oh my zsh for sure, but startup time, as long as it's less than ~5 seconds, is not likely to be a big problem for most people. On slower machines the real killer is the time it takes for the next prompt to appear after each command. There are mitigations, but I've definitely had machines where it is almost unbearable.

Five seconds really is unusably slow, in my opinion. Even a tenth of that is grating. If I want to run a one-off command, I open a new terminal to do so, and close it when I’m done.

My pretty extensively customized zsh config with ohmyzsh takes maybe a quarter of a second to load... Maybe I don't have as customized config as I thought because if it is taking a second or more I can't imagine what is being loaded.

omz is very very heavy weight. Back when there was a freenode and #zsh it was widely hated as only IRC can hate something.

You can source



without omz anyways.

Yes it is incredibly heavyweight, but it's very batteries-included in its approach, which helps zsh newbies get started.

For those who want to shed the heavyweight omz stuff, I recommend zplug [0]

[0] https://github.com/zplug/zplug

Or zgenom[0]. Or if you want to get ESPECIALLY froggy and async with your shell startup, zinit[1]. I ran zinit for years before moving to zgenom. A large swath of electron apps use some weird NPM library to “resolve” your shell environment by kicking up a full interactive shell and then reading the environment variables out of it. zinit messed with that and would hang a lot because of all it’s async loading.

[0] https://github.com/jandamm/zgenom

[1] https://github.com/zdharma/zinit

Avoid oh-my-zsh like the plague if you value performance...

Here's a nickel kid. Buy a real computer.

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

The shfmt parser seems re-usable: https://github.com/mvdan/sh (syntax subpackage).

Not really what you're asking for, but a language that compiles to bash for portability reminded me of Batsh [0], a C-like language that compiles to bash for Linux/Mac and batch for Windows.

[0] https://github.com/batsh-dev-team/Batsh

> much better than any Unix-style shell

TIL zsh is even better than zsh!

the only thing where I found zsh stupid, was withing psql where tab completions somtimes add an \ which will kill my query.

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.

Finally, a command line shell for the 90s

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.

> Where do you put your function definitions?

Inside ‘function’ blocks, like the language syntax defines for any other type of function. I can then throw those functions into any file I chose.

Having special functions inside special files just creates annoying special cases I need to look up.

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.

There's also this package, which the author admits only allows "slight" customization, to implement sessions: https://github.com/farzadghanei/fishion

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.

Ok. That sounds a lot more sane then. Thank you for the clarification.

You can literally still do it the way you want, there's just a convention that `fish` uses to autoload so you don't have to if you don't want to.

It's as simple and seductive as Ruby on Rails was 20 years ago.

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.


So yeah you could make crazy stuff like Rail's routing and rendering DSLs that interpreted method names as programs.

Bash and zsh have basically the same thing.

It's meant to give you a nice error message like

"foo is missing. You can install it with 'apt install foo123'"


- https://zsh.sourceforge.io/Doc/Release/Command-Execution.htm... for zsh

- https://www.gnu.org/software/bash/manual/bash.html#Command-S... for bash

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.

Setting a specific path as an “autoload” path to load shell functions is in bash and zsh too?

Yeah, but there aren’t magic function paths that you need to do basic things like setting the prompt.

Don’t get me wrong, I think the PS1 variable sucks. But I’m not convinced Fish’s solution is much better.

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)
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.

So no, I'm not a fan of fish.

`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

[0]: https://starship.rs

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?

Here's mine: https://github.com/maximum-ethics/linode-caddy/blob/master/r...

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’ve been using fish for years and I love it. I have a simple prompt and a few functions that I share between all of the environments I have.

I've had this experience as well. My zsh config was a monster. fish was extremely easy -- starship and ready to go.

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.

Interesting — I picked fish because it was so much faster than zsh but I also run it pretty lean without much in the way of plugins.

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.)

[0] https://github.com/olets/zsh-abbr



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]

Seems to work in bash too, fwiw.

[0] https://fishshell.com/docs/current/interactive.html#copy-and...

The first thing in install with oh-my-fish is bang-bang, which fixes this issue for me.


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 use zsh because of its tab completion lol

Totally. And there’s nothing better than fzf-tab


I'll try it out!

Totally agree, it’s very off putting to use zsh auto complete after years of using bash.

Ah cool thanks!

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!

I use fzf for reverse search and it works in fish with ctrl+r binding.

I'm guessing someone probably has a plugin for it or people just get used to using autosuggest

What specifically are you looking for? It has history search but it sounds like you’re looking for something specific?

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 used to use Control-R a lot and now use the fish equivalent:


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).

In fish: Just enter the text you want to look for into the commandline and press [up].

There's no need to enter a separate mode via ctrl-r.

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.

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!

Minor nit, but you can compute integer percentages from integer inputs with bash, without shelling out to an external program.

You can also compute non-integer percentages by just adding the period after the math.

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.

Or use powershell and have all the power of C# at your finger tips and shell scripting at the same time!

Powershell does have some really cool shit as far as scripting languages go, but it's just so damn verbose.

It comes with a lot of aliases and does fuzzy matching on arguments. For example:

    gci -r

The -r will fuzzy match to -Recurse. It’s a change going to an OO shell from bash but I run it on windows and WSL. The windows integration is really good too.

Or use xonsh, which has all the power of Python and shell scripting at the same time!

Xonsh is damn impressive and I loved playing around with it. But it is really slow.

Off topic, but I do like powershell. The lack of a reasonable way to have it NOT pop a console window is regrettable though. What is provided still pops a console window, just for a shorter amount of time.

Massive pain point. Here's a workaround though: https://github.com/stax76/run-hidden

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.

Agreed - people have told me that other shells are better (and I'm sure they are better than bash in some ways) but for a shell, ubiquity is arguably the most important feature.

I agree. There's perfectly good scripting languages if I need to do anything beyond bash.

Also agree. For me, I want to be able to do simple "for each file run this command" loops in the shell. But if those loops get much more complicated than that then I switch over to Ruby.

*Tilt whisky glass your way.

"Bravo sir."

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).

> 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.

What about adding only these functionalities you may care about?

When I tried zsh, what I liked was the history search. Like you, everything else "wasn't as simple as I expected". So I fixed my bash to add and expand what I had enjoyed!

Check https://github.com/csdvrx/bash-timestamping-sqlite :

- stores everything into a sqlite database so separate bash in separate terminals can access each other history on the go (without waiting for the session to end and the history being committed),

- add extra details to the history like when the command started, stopped, which with return code, in which directory (more on what that enables below),

- for accessing the history, uses fzy for fuzzy finding, (the one thing I mostly enjoyed in zsh, not zsh but fzy!!)

- provides 2 separate history search contexts: either global (ctrl-t) or "this directory only" (ctrl-r), with extra goodies like excluding commands with a non-zero return error code thanks to the extra things saved

I included a few examples of the SQL queries you can run.

Looks awesome. Thank you for building it.

> We had _just_ settled the emacs vs vi argument when this starts ...

We did? That’s news to me!

Yes, we are all switching to vscode.

I switched from vscode to (doom) emacs. I don't know how it is now, but the vi-like key bindings of vscode were pretty bad at the time.

I hate to admit I'm in that camp. Used emacs because it has great support for editing over ssh. When vscode came out with remote connections for ssh and wsl, I switched over. Emacs is a great operating system-I mean editor, but it's pretty hard to extend it to have the feature set of newer editors while maintaining reasonable performance.

It takes a while for the time spent learning emacs/vi to amortise itself. You need to learn the key bindings, the ad hoc scripting language, and the substantial API. And throughout it all, there's the constant temptation to switch to something easier like vscode.

I tried learning to use Org-mode as a Markdown replacement over Doom Emacs. At some point, I switched back to vscode+Markdown and it felt like a breeze. I'm not a SWE, so the payoff would've probably taken too long to happen.

I also tried using the Maxima mode for Emacs as an interactive CAS. The only thing I got was Latex-formatted output and the same Emacs-like key bindings that you get in any terminal emulator. No auto-suggestion/Intellisense to help you navigate and avoid learning the API, so I wasn't overly impressed.

I have been using vi like key bindings for the past 15 years, so learning to do differently would be a significant investment I'm not willing to make, even moreso that the benefits would not be clear ;)

But yes, I'm 100% convinced that it's not for everyone, and depends a lot of the quality of the integration/mode of your specific use case.

Vim bindings are pretty good if you use the nvim plugin in vs code. It's not emulating (n)vim, you need to have the actual nvim installed and it will use that to provide the keyvindings.

I also switched from vs code to doom emacs though, just because I wanted to.

I use code folding and find that abysmally broken with the normal vim mode extension in vs code. Is it any better with nvim?

A decade ago after noticing such a conversation I'd have thought about and explored about which was better and which one to try and switch to; at least for few weeks until the novelty wore off.

I am older now (not sure smarter or lazier as well or both) and at this point I couldn't care less. As an Android developer I am essentially forced to use Android Studio - a rebranded IntelliJ and a shameless resource eater - it does the work and employer pays for better Macs every now and then, so it's good.

Who needs vscode when you can just hand write it and scan it?

I know this is a joke, but I actually used to handwrite all of my programs.

Back then, I would then keypunch them the next time I was on campus and could access a keypunch machine. Writing even a modest 1000 line program was quite a chore because you'd have to carry around a box of 1000 cards and keep them from getting mixed up. Here's a picture of a full 2000 card box[1]; notice how we used to write diagonal lines on the edges of the cards to help put them in order after spilling the box on the floor!

See [2] for a picture of the first kind of keypunch machine I ever used, the IBM 026. The subsequent model, the IBM 029, was much better.

Of course the turnaround time wasn't measured in seconds back then. Once, I was given an assignment that required writing a program in IBM 360 assembler, it was the first time I ever had to write assembly language. With the aid of IBM's excellent manual, IBM 360 Principles of Operation, I hand wrote this program, went to campus to keypunch the program, and turned it in for one of the seven scheduled overnight submissions we were allowed. After around the fifth attempt I was starting the think that I wouldn't get credit for this assignment; miraculously, on my seventh and last submission the program finally worked.

[1] https://upload.wikimedia.org/wikipedia/commons/thumb/8/86/Pu...

[2] https://en.wikipedia.org/wiki/File:Keypunching_at_Texas_A&M2...

On my case, it is more like being dragged into VSCode than actually switching to it by choice, specially when Microsoft makes some stuff available as VSCode plugins only (e.g. Powershell and ARM templates editing).

The Remote Development extensions being locked away is the kicker for me. Everything else I've been able to find on alternative "markets" (can we just call them repositories? we're all developers here...)

People who switch to vscode probably also switch to fish anyway.

Hmm, in this false analogy game, what editor is aligned with korn shell? xemacs?

> probably also switch to fish anyway.

I feel attacked. In all seriousness: so long as you have a POSIX shell available (and set as default), you don't have to use a POSIX shell. I rarely do anything with fish syntax because I mostly use it for the REPL experience.

> Hmm, in this false analogy game, what editor is aligned with korn shell?

vi (not vim), preferably running on OpenBSD :)


OpenBSD (as well as the others?) come with pdksh and nvi out of the box.

Probably closer to vile or elvis if we're talking proper ksh93.

I was simply going to do:

        $ cat > file.c <<EOM

Just use ^D to end, saves you writing here `<<EOM` and EOM there.


And still there are emacs users.

I'm just gonna use fish because I can't be bothered to setup zsh.

From the article... > P.S. maybe fish is even better by the way, but I could never get over the bright colouring and all these things popping in and out of my screen; it's such a "busy" chaotic experience! I'd have to spend time disabling all that stuff to make it usable for me, and I never bothered – and if you're disabling the key selling points of a program then it's probably not intended for you anyway.

I have no idea what "things popping in and out of my screen" could possibly mean. I use Fish and it's a beautiful, calm experience.

It does all these "autocomplete" things; for example I type "ls" and then I automatically get "| less" after that (why "| less"? Not sure), also the colours of the text of what I'm typing is constantly changing based on what I typed.

I appreciate that some people really like this sort of thing and that great, but it's not for me – I find it very distracting and "chaotic". Completion is brilliant, but I only want it on-demand (same with autocomplete when writing code).

>why "| less"? Not sure

Pretty sure it's autocompleting the last thing that you executed that started with "ls".

That's exactly what it's doing.

Not sure where it's getting that from, because I installed fish just for that comment and I don't recall running it any time recently (not for years), and even after deleting all "fish" related things I can find in my ~ it keeps doing it, hmm...

Anyhow, doesn't really matter shrug.

Fish is really great for an interactive shell, I love using it. Not so great when it comes to scripting though. For example I think there's still no way to easily exit a script on error except to manually check exit codes.

However I haven't tried writing scripts in zsh yet, just sh/bash.

I couldn't possibly use a shell that wasn't also a decent shell script. It seems my natural progression is always:

1. "Hand written" commands in shell

2. Shell script with copy-pasted commands from interactive session

2b. Evolved shell script with getopt. Complexity level beginning to tickle the spidey sense that it's too big.

3. Node.js script (I can write Python but JS seems infinitely nicer)

fish would break the 1->2 transition, sending me straight to 3 before I've really gotten a handle on the problem domain.

Perhaps it's just personal preference, but in what world is JS "infinitely nicer" than python?

I'm really far from a JS fanboy, but Python's whitespace thing really is annoying enough that I avoid it when possible.

Ruby'd be my pick for "most-pleasant scripting language" so long as I can avoid any libraries/frameworks that do too much metaprograming "gee, wouldn't you love to never be able to track down where this is defined?" crap. Maybe Lua, but deps are a bit of a pain to manage there, and it's a huge step down in third party library availability compared with the other three languages here mentioned.

Really, if not for up-to-date library availability being higher in other languages, I'd probably still be using Perl for everything. It feels more "command-line native" than the other options. Installed everywhere. The community's not too in-love with trendy, cute bullcrap. A project you wrote five years ago will probably still run fine with no effort. It's kinda like supercharged bash with way fewer footguns.

I've been seeing a lot of pro-perl comments here on HN. Never used it myself, may have to check it out.

Beware that it was created at a time when many programs were written in plain text editors, with no syntax highlighting or autocompletion, with the printed-book language manual on your desk, on a screen that could legibly display only a small fraction of the text that a modern display can (or even ones that came along a few years later). Hence: sigils for variables (though unlike PHP these make some sense and are actually used to do things, and aren't just there because... well, because Perl did it, so PHP should too, I guess); single-character magical variables for very commonly-used loop- or input-related variables; et c.

I don't think anyone would make something quite like it these days, starting form scratch.

Sorry, I didn't mean to start a flame war. I find regexes, HOF, and especially asyncio/Promises much more palatable in JS, which are things I look forward to when moving out of shell scripting land.

Nothing precludes you from using bash scripts even if ysit fish interactively. Just set a shebang in the script, or run it with bash script.sh.

That said, personally, for anything more involved than a one-liner alias kind of script, I turn to python, or PowerShell on Windows.

> for anything more involved than a one-liner alias kind of script, I turn to python

I agree with this. If you can trust your users to have python3 in their environment, I don't see a good reason to use sh/bash instead of python. Non-trivial shell scripts are unreadable to me, and the semantics are too different from normal programming languages.

The only scenario I can see is a script that has to call multiple commands over and over, which can be a pain in python if you're piping a lot of stuff and all that. No, piping grep to cut and sed doesn't count; you can do that in python itself.

> If you can trust your users to have python3 in their environment, I don't see a good reason to use sh/bash instead of python

You would need to ensure said users have all the pip dependencies your python script imports as well. shell scripts don’t have that issue unless you’re calling some external program that isn’t installed.

>shell scripts don’t have that issue unless you’re calling some external program that isn’t installed.

Good thing that bash has such an extensive amount of standard libraries and that you're almost never calling external programs? And unlike Python, which is seemingly almost exclusively calling third-party libraries from PyPI?

> That said, personally, for anything more involved than a one-liner alias kind of script, I turn to python, or PowerShell on Windows

You and submitter (who according to CV is proficient in Python) might like Oilshell.

I made dash my sh and started writing shell scripts targeting it instead of bash. If there's some bash-ism that can't be elegantly expressed in POSIX sh, it's time to bring out something like Python.

zsh can be a real pain to setup if you want a "performant" git status in your prompt. vcs_info is just too damn slow.

I prefer minimal customisation and so tend to stay away from heavy plugins like ohmyzsh, though I have friends who use it and really like it.

https://github.com/romkatv/powerlevel10k claims to do this (and other things) while remaining extremely fast (apparently the dev was aiming to keep it below the threshold of human perception; not 100% sure that extends to all features or not)

For those not using zsh, https://starship.rs/ is an excellent shell prompt which works with several shells.

Using it is very high value-for-effort.

Use https://starship.rs/ and get a performant prompt that stays the same when you switch between bash, zsh and fish.

I suppose that's the nano equivalent:) And that's not the insult you might expect: It sometimes is viewed as a toy, but it actually has depth and does a lot more than one might expect, all while staying very user friendly, albeit at the expense of being rather different than the other major options.

One still needs oh-my-fish, as one needs oh-my-zsh. So the setup time is 100% equal for fish and zsh.


https://github.com/ohmyzsh/ohmyzsh/ , https://ohmyz.sh/

Despite the similar names, what oh-my-fish and oh-my-zsh provide isn't comparable.

E.g. oh-my-zsh adds a bunch of keybindings and a git prompt, and sets a bunch of options (like turning on history deduplication, or comments in interactive sessions).

Fish already does equivalents of much of that, by default, so oh-my-fish doesn't have to do it. They mostly add more prompts (with their own, separate, git prompt implementation) and provide a package manager.

That means fish without oh-my-fish is already close to the experience of zsh with oh-my-zsh.

(disclaimer: I'm a fish developer, and not really involved with oh-my-fish - I sent a few courtesy patches over the years)

I very strongly disagree that you need or even want OMZ. You don't need it for Bash, why would you need it for Zsh?

It might be nice when you're getting started, so you don't have to learn all the ins and outs of the shell to have cool prompts and other useful functionality.

    ~ > omf
    fish: Unknown command: omf
Apparently one does not.

Briging Korn shell into the party.

csh and tcsh dialled in.

The more the merrier.

Shell wars are as old as editor wars.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact