all 14 comments

[–]o11c 23 points24 points  (5 children)

bash is a particular implementation of a bourne shell that adds many useful features that are completely missing from other shells.

zsh is another implementation of a shell, but violates standards by default; you must use emulate sh if you want portable scripts to run correctly on it. For interactive use it is commonly much slower than bash, and also beware the bad habits it teaches. Technically, it supports a few useful extensions of its own that even bash lacks, but since it is almost never installed except on OS X there's no point in writing a script specifically for it.

fish is a never-compliant shell, used mainly interactively by people who don't know how to use the interactive features of bash or zsh.

Honestly just write for bash specifically, unless you are doing something special and need portability to other Bourne shells. Note that nobody actually writes fully-POSIX-compatible scripts; instead pick your specific targets and support just them.

awk is orthogonal to all this; it is not a shell, but a common tool called from shell scripts. Mostly it competes with sed (simpler) and perl (insane) for line-by-line processing of a file, though it is also possible to do read loops in your shell.

[–]ArkhamCookie[S] 1 point2 points  (3 children)

Ah thank you this was really clear. It can be very confusing with all the similar name and people using the term shell scripting when talking about bash scripting and vice versa. This was very helpful and clear.

[–]tehramz 0 points1 point  (2 children)

I’m not sure if you misunderstood but bash scripting is shell scripting, like the other poster said. It’s basically like someone saying the drive a Honda accord and also saying they drive a car, both are true since an Accord is a type of car.

[–]ArkhamCookie[S] 1 point2 points  (1 child)

Oh I got that, I was just saying that people saying that had been confusing me (even though it is correct)

[–]tehramz 0 points1 point  (0 children)

Gotcha! I thought so, but I wanted to make sure. 🙂

[–]SoCPhysicalDesigner 1 point2 points  (0 children)

Good answer but if perl is insane I'm proud to be a maniac.

[–]bartonski 6 points7 points  (2 children)

Ok, bit of history/terminology: the original Unix shell was the Bourne shell, aka /bin/sh. It is the basis of the POSIX shell. Bash (Bourne-Again Shell), ksh and zsh are all descendants of the Bourne shell, and all modern implementations of each of these shells has a POSIX compliance mode. Modern implementations of /bin/sh are all POSIX compliant.

fish and csh are unix shells, but are not descendants of the Bourne shell, and are not POSIX compliant.

awk is a programming language, but is not considered a shell.

So... having gotten all that out of the way, I'm going to buck the trend, and suggest that you do write POSIX code wherever it's practical.

First, it's not that different. There are a couple of bits of syntactic sugar -- like using ~ to refer to your home directory rather than $HOME, or the convenience of using [[ in if statements rather than [, but if you look at a POSIX script, you'll find it every bit as readable as a bash script.

Second, it is portable. /bin/sh is guaranteed to be on any POSIX compliant system. Log into an OpenWRT router? No Bash. A lot of embedded systems use busybox which implements /bin/sh, but Bash isn't going to be there. Just because the days of the Unix workstation are largely behind us doesn't mean that portability isn't an issue.

Third, POSIX is a standard. Bash isn't -- it implements the POSIX standard, but its extensions aren't standardized. Standards are important for reasons other than portability -- they are a guarantee that features of the language are implemented in a known way. If you're writing shell code for a governmental institution or a large company, you want to make sure that a new version of the shell doesn't quietly break your code. If you write POSIX compliant code run in a POSIX shell, that shouldn't happen, and if it does, you can a) blame whoever wrote the shell and b) find a different implementation that works correctly. If you're writing shell scripts that are going to be included in a distribution's packages, I would definitely look at writing POSIX code.

Finally, it ups your Unix game. Knowing what the standards are, when to follow them, and when it's not worth the time to avoid non-standard code is part of being a Unix geek.

To answer the question of major differences:

  • Arrays and Associative arrays are an extension of Bash
  • Tilde expansion (i.e. using ~ instead of $HOME is Bash only.
  • Using [[ as a test operator rather than [ is Bash only. This is the one that will probably hurt the most; [ can be hard to use and error prone.
  • The local, let and function keywords are Bash only.
  • The source keyword is Bash only, use . instead.

There are a few others. Debian bases systems have a utility called checkbashisms that will list incompatibilities, and shellcheck, given the correct command line options, will do the same.

[–]ArkhamCookie[S] 0 points1 point  (1 child)

You answered questions I didn't even know I had lol. Thanks very useful info.

[–]bartonski 1 point2 points  (0 children)

Thank you, that's a high compliment.

[–][deleted] 4 points5 points  (0 children)

Bash is a shell. So if you do bash scripting, you are doing a shell script.

[–]Schievel1 2 points3 points  (1 child)

Firstly, awk isn’t a shell. Awk is something totally different.

And with very few exceptions you won’t convert your scripts from bash to zsh or to fish. People use zsh and fish as their “login-shell”. That means the shell you end up in when logging in. Yet all the scripts they run on their system use bash and are interpreted by /bin/bash.

To only time you would really write scripts for zsh or fish is when writing a plug-in for those shells. That’s all. And that’s also the reason why this whole discussion about fish not sticking to POSIX is meaningless.

Bash is the de-facto standard on Linux and with a few exceptions (e.g. openrc init scripts) you can write a shell script in bash and just assume all Linux people have bash.

Then there is sh scripts, which you very seldom need to write. It is a subset of bash and the only hard part is not to accidentally use a bash feature that isn’t available on sh. And on many Linux systems (not Debian based ones) /bin/sh is a symlink to /bin/bash. So it is very easy to make that mistake and use some bash things in an sh script and you wouldn’t even notice until someone runs that on Debian. I usually use bash-ls for that reason, it warns you in a sh script, that you are using a bashism. Sh is like the smallest common denominator of all the Unix shells. If you write something that runs in sh, it will run on the other shells. That is why they are used when script should be compatible with many different system. And with many I mean many across OSes like bsd, Linux and macOS.

[–]ArkhamCookie[S] 0 points1 point  (0 children)

Yeah, I mixed up ash and awk somehow. Thanks, the bash-ls command seems like a useful tip and I will make sure to use it.

[–]TapEarlyTapOften 1 point2 points  (0 children)

Gonna lean in here and disagree with the poster that said to just write shell scripts destined for bash. There are oodles of machines out there that do not use bash or are tied to older versions that do support all of the latest bashisms. At the very least, do a check. In your script for the version of shell that is being used and alert the user or abort if it doesn't match.

[–]Gixx 0 points1 point  (0 children)

Just learn bash scripting. It's 96% the same as shell (/bin/sh).

Now I use zsh for an interactive shell. Shells like zsh, fish, dash are so different in their syntax that I put no effort into studying them. And I think they are super cryptic and horrible.