Bash vs ZSH

Ok I’ll bite :lol:

What are the differences/benefits of one over the other? :101:


First, I’m using “vanilla” zsh rather than oh-my-zsh (which is a config framework, similar to what spacemacs is for emacs).

The main reason I did the switch years ago, was because of the much better tab-completion experience that oh-my-zsh provided (back then I used it).

Also it had the double glob for recursing directories (**/*.ex) which did not work for bash back then and today is only available after explicitely configuring it.

Another problem that I often had with bash and globbing was that I hit the “max command length limit”-wall, since some globs easily created commands much longer than 10k characters, and I do not even know how small the limit is or if one could change it, but I never hit this limit with zsh.

Last but not least, in theory, I could filter globs in zsh by file age, size, folder or file or sym link or whatever… Without the need to use find, but I rarely use those extended globs, as I can not remember their syntax and therefore have to look them up everytime.

In theory, it could live update its prompt and show a live clock at the current prompt.

As I am using vanilla today I have to manually keep my plugins up to date, but that is not a problem.

My most loved plugins are zsh-completions and zsh-syntax-highlighting which syntax-colors the prompt on the fly. This is a nice thing when often dealing with constructs that are a bit more involved than just a copy or deletion.

My third most plugin is not an argument for zsh, since there are powerline themes for bash as well :wink:




Sorry, I choose neither. For many years I tweaked my ZSH configuration and carried around a theme file. Eventually I decided to try Fish and was delighted to find it did everything I had configured ZSH to do right out of the box:

  • command completion (git, brew, asdf, etc)
  • git branch tracking
  • history completion
  • sane looping and function declaration

The only thing I have done to configure it is tweaking my prompt slightly.

The only two downsides I have encountered:

  • Occasionally I’ll drop into bash just to paste a command that doesn’t play nicely with fish
  • I need to remember to use env to set some variables.

I try fish every now and then, but it has two major downsides for me:

  1. its alias handling is below par. Sometimes I need to look up what an alias would expand to, I can’t do that with fish.
  2. its syntax is so different from regular shell, that context switches between my own terminal and bash driven terminals at work get too hard, also you have to translate about every snippet you find in the internet because of this.

For me fish is entirely for command line usage where the most complex thing I’ll script is a small loop. I still write all of my scripts to be portable, either in sh or bash if I can get away with it.

It has been difficult to convert most engineers I work with as well. The minor inconvenience of not being able to paste something from a README or SO is enough to throw away the day-to-day efficiency gains.


I can extend zsh to give me what I miss from fish, but I can’t add proper alias handling to fish, there is no loss for me.


PS: you shouldn’t ask such kind of questions :wink: you could have asked whether to use emacs or vim as well :wink: Such questions tend to get answered with a grain of religious beliefs :wink:


I can’t live without the per-directory-history plugin that comes with oh-my-zsh.


What @sorentwo says.

I switched from Bash straight to Fish and it nicely works out of the box (although I admit that I use oh-my-fish and fisher as plugin/config managers for themes and prompts).

But I agree with @NobbZ, too. It’s really annoying that the syntax is so different from bash.
Lots of times I got tripped up by this.

But it’s still a really nice experience because I most use it for standard command line stuff where the commands are the same in bash and in fish.


Indeed! I just use the default which comes with the distrib I installed, and thus bash. For anything more complicated than piped command lines I use python anyway, which preserves my sanity.


I do not get what is the problem there, type my_alias prints generated function without any issues.

POSIX shell, there is no such thing as “regular shell” as there is a lot of different shells (csh for example that has enormous influence on other shells, including bash and fish).

Not in Fish 3.0 as they have added support for && and || so almost all snippets works without any problems.

My biggest problems with Fish shell (which I am heavy user for about 6 years) are:

  • No shell substitution for data input, so while I can do something like source (foo | psub) that will source output of foo command (in Bash it is source <(foo), but I cannot do something like foo | tee >(foo) (Bash syntax) right now. This is sometimes problematic as I need to fallback to tools like pee for that (ex. I have script ix that uploades provided file to the service, and it would be really useful to print resulting URL to the shell as well as copying it to the clipboard, unfortunately it is not possible in easy way.
  • Functions do not run “in parallel”, which mean that shell function will output text only when it ends, not as this is running. That mean when I create function function tak; yes tak; end and I will try to run it in the pipe, like tak | tee then it will not output anything until I kill everything. It is problematic, as I cannot write file watcher function.

And I assume you will get more auto completion in zsh/bash vs fish.

For example k8s
Enabling shell autocompletion (Bash on linux, Bash on Mac, Zsh) but no Fish :disappointed:

But any way I would stay with Fish :slight_smile:

Whoa, so it’s impossible to do something simple like print status updates or a progress bar?! o.O

/me is a ZSH user

Just compare zshs behaviour with what I get from fish:

zsh $ alias ll
ll='ls -ah`
fish $ type ll
# Defined in /usr/share/fish/functions/ @ line 4
function ll --description 'List contents of directory using long format'
    ls -lh $argv

In ZSH its so much easier to see what the alias actually is. Also I can make a distiction between an alias and a function.

That is the main difference, in ZSH you have aliases and in Fish there are only functions, and alias is also a function that defines other functions. So unless Fish add some “special handling” for aliases (I do not believe it will as it would be against idea of the Fish) then you will not be able to get the same result as in ZSH. Everything to make shell simpler with less corner cases.

You can print progress, what do not work right now is using Fish’s function to be run as “parallel producer”. I cannot describe it better so maybe you can check this issue.

1 Like

Ah! You meant multiple inputs to a single output stream/pipe! Yeah that is pretty trivial in bash/zsh/etc, especially via backgrounded processes… That’s supposed to be a standard posix thing that streams can be multiple I/O connectors, so it is exceptionally weird that fish doesn’t follow that… o.O

That first post on that issue was very off the mark, I don’t think it would have anything to do with fish needing to execute its bytecode in parallel, you can easily background ‘other’ processes as well and they should still be attached to the same output pipe. I’m guessing that they are just creating new pipes and listening on them directly instead of passing around the pipe kernel handles as it should be doing?

That’s a very weird to read issue… O.o

Hmm, so it seems they background a process when & on it but they don’t background their own interpreter when doing the same (or emulate it as all modern shells do), that’s a very bad misdesign there and breaks the standard posix expectations… o.O

EDIT: So what features does fish have that zsh doesn’t via plugins (zsh is by default very simple but very pluggable, far more so than bash)?

Not exactly. Fish do not start subshell on & and this is the root of the problem while ZSH and Bash does (just run echo $$; sh -c 'echo $PPID' & to check that out). This simplifies few things, but causes problems like this one.

1 Like

Well actually ZSH and Bash ‘pretend’ to start subshell’s, but they emulate it only as far as needed to fake it before they eventually end up spawning a real subshell if the need actually arises (surprisingly not very often). It seems that whole faking it step is what Fish is lacking. :slight_smile:

1 Like

I’m also a big fan of fish. I tried zsh numerous times over the years but never felt at home with it. Something about fish just clicked for me, I’m very glad I tried it :slight_smile:

I wouldn’t use it for writing scripts, I use #! /bin/sh for those.

I’ve actually found fish has better auto completion thanks to its parsing of man-pages to determine flags.

Of course this is only possible for software that includes a man page, something which seems to be less popular now…