Since the default Shell on almost all LinuxDistributions is bash(1), this page lists some common bash-isms that might fail on other sh-type shells on other Unix(-like) OperatingSystems. If there is even the remote possibility that your script might be used by someone other than you, it's worth putting in a small bit of effort to use more portable constructs.
This little section is quoted from the documentation for autoconf(1) (do info autoconf, although personally I hate info pages). This demonstrates how stable the Unix programming environment is (don't use "new" features added since 1977!).
Portable Shell Programming
When writing your own checks, there are some shell-script programming techniques you should avoid in order to make your code portable. The Bourne shell and upward-compatible shells like the Korn shell and Bash have evolved over the years, but to prevent trouble, do not take advantage of features that were added after UNIX version 7, circa 1977 .
You should not use shell functions, aliases, negated character classes, or other features that are not found in all Bourne-compatible shells; restrict yourself to the lowest common denominator. Even unset is not supported by all shells! Also, include a space after the exclamation point in interpreter specifications, like this:
#! /usr/bin/perlIf you omit the space before the path, then 4.2BSD based systems (such as DYNIX) will ignore the line, because they interpret "#! /" as a 4-byte magic number. Some old systems have quite small limits on the length of the #! line too, for instance 32 bytes (not including the newline) on SunOS 4.
Note that test(1) is a built-in function in many shells.
In bash, a non-existent variable is treated like "", but on some other shells it is treated like nothing.
$ test -z $NON_EXISTENT_VARIABLE $ echo $? 0
$ test -z "$NON_EXISTENT_VARIABLE" $ echo $? 0
$ test -z $NON_EXISTENT_VARIABLE test: argument expected $ echo $? 1 $ test -z "$NON_EXISTENT_VARIABLE" $ echo $? 0
This seems to be a GNU extension – some shells don't allow -e as an option to test.
$ test -e filename test: argument expected
In most cases, you can use -r (for file is readable) instead.
$ export VAR1=foo $ echo $VAR1 foo
$ VAR2=bar; export VAR2 $ echo $VAR2 bar
$ export VAR1=foo VAR1=foo is not an indentifier $ echo $VAR1 $ VAR2=bar; export VAR2 $ echo $VAR2 bar
$ tar xf file.tar (gnu) $ tar -xf file.tar (bsd)
Not all tars support the -z option for gzip(1)ped TarBalls. Pipe it instead. (Not all environments have the zcat(1) alias either.)
$ tar -zxf file.tar.gz # not portable $ gzip -d -c file.tar.gz | tar -xf -
BSD-style unixes uses flock(2), which uses "advisory" locks. Ie, a process with sufficient read or write permission can ignore the lock and read/write a file. SysV-style unixes use either advisory locks or "mandatory" locks (if enabled in the FileSystem), and access them by the fcntl(2) command and a struct flock object.
A more portable way (POSIX 1003.1-2001) is to use the lockf(3) function from unistd.h, which will do the correct type of locking for the unix it is compiled on. (In Linux/GNU libc, this function is a wrapper around fcntl(2)).
uint64_t x; printf("%llu",x);
This however will fail on 64bit machines as uint64_t is long not long long.
uint64_t x; printf("%"PRIu64,x);
Other peoples types change in size, sometimes at the flick of a #define. One example is off_t changes if LARGEFILE64 is defined under Linux. Use the c99 <stdint.h> types (uint64_t) so that they don't vary. If you absolutely must reference someone elses type, do so via a pointer (eg FILE*) and treat it as opaque.
Part of CategoryProgramming
3 pages link to PortabilityNotes: