Your bash
scripts should work on any machine that has the bash
shell installed, unless you use bash
features that are too new for the version of the shell on the other system, like for example, trying to use name references (declare -n
), associative arrays (declare -A
), or the %(...)T
format of printf
(or any of a number of other different things) with the default bash
on macOS (which is ancient; just install a newer bash
with Homebrew in that case). Your scripts may also fail if they use external tools not available on some other system, or features of tools that may not be implemented, or that are implemented differently.
The most portable shell language on POSIX systems is the sh
shell. This is described here: https://pubs.opengroup.org/onlinepubs/9699919799/idx/shell.html
The bash
shell (and others) implement sh
, with extensions. Some shells, like fish
or zsh
, are not even trying to be sh
shells at all (but zsh
provides a degree of "sh
-emulation" via --emulate sh
), while shells like dash
does try to be more pure to the POSIX sh
standard (but still with a few extensions).
In general, shell aspects that relate to syntax and built in features should be "portable" as long as the script is always executed by the same shell (and version of the shell, to some degree), even if that shell happens to be the fish
shell. This is the same type of "portability" that you have with Python, Perl and Ruby scripts. There should however always be an sh
shell available (usually installed as /bin/sh
), which is usually bash
, dash
, or ksh
running in some form of "compatibility mode".
The most portable utilities on a POSIX system are the POSIX utilities. These are described here: https://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html
GNU utilities, the ones in coreutils
, implement the POSIX utilities (which, as user schily points out in comments, does not mean that they are POSIX-compliant), and then expands upon them with features that are mainly for convenience. This is also true for the corresponding utilities on other Unix systems. Also note that the GNU coreutils
package on Linux does not contain all utilities that POSIX specifies and that utilities such as find
, sed
, awk
, and others, which are all POSIX utilities, are packaged separately.
As long as you stay with utilities that are POSIX and use their POSIX behaviour (in terms of options etc.), in scripts that use POSIX sh
syntax, you are likely to be "mostly portable" across most Unix systems. But also note that there are non-POSIX utilities and non-POSIX extension to POSIX utilities that are still fairly portable, because they are commonly implemented. Examples of common non-POSIX utilities are pkill
, tar
, and gzip
. Examples of non-POSIX extensions to POSIX utilities that are common to find is the -iname
predicate of find
and the fact that sed
often can be made to understand extended regular expressions using -E
.
You'll learn what extensions in implementations of what tools, running on what Unices, behaves in what way, by testing by yourself (in virtual machines for example), and by reading questions and answers on this site. For example, How can I achieve portability with sed -i (in-place editing)?
Also note that there are instances when the POSIX standard leaves a behaviour "unspecified", which mean that the same utility on two different Unix systems, using a POSIX option, may behave differently. An example of this is found here in Why permission denied upon symbolic link update to new target with permissions OK?
The disadvantage of trying to write pure POSIX shell code is that you miss out on some really useful features. I once tried to write the POSIX equivalent tests for find
that GNU's -readable
predicate tests for, which wasn't easy, and trying to signal a process by name using ps
and grep
instead of pkill
is a topic of a few too many questions on this site. Likewise, trying to "stay POSIX", you would need to stay away from really useful things like perl
, and you would not be able to do much of any administrative task portably (adding users, manage backups, transfer files between systems, etc.)
In general I prefer a pragmatic approach to getting things done, while understanding that my code may have to be modified to support other systems later (if it's ever used on other systems), rather than a purist approach. This does not stop me from writing scripts that I know will have as few portability issues as possible (I wouldn't use unportable semantics everywhere). But that's my personal opinion.
shellcheck
a "portability compliance checker". – Kusalananda Dec 02 '19 at 12:54[[ .. ]]
if the hashbang shows/bin/sh
. – ilkkachu Dec 03 '19 at 11:50