Go to the first, previous, next, last section, table of contents.
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/perl
If you omit the space before the path, then 4.2BSD based systems (such as Sequent DYNIX) will ignore the line, because they interpret `#! /' as a 4-byte magic number.
The set of external programs you should run in a configure
script
is fairly small. See section `Utilities in Makefiles' in GNU Coding Standards, for the list. This
restriction allows users to start out with a fairly small set of
programs and build the rest, avoiding too many interdependencies between
packages.
Some of these external utilities have a portable subset of features; see section Limitations of Usual Tools.
There are several families of shells, most prominently the Bourne family and the C shell family which are deeply incompatible. If you want to write portable shell scripts, avoid members of the C shell family.
Below we describe some of the members of the Bourne shell family.
foo= false $foo echo "Don't use it: $?"
cat ${FOO=`bar`}
BASH_VERSION
is set. To disable its extensions and require
POSIX compatibility, run `set -o posix'. See section `Bash POSIX Mode' in The GNU Bash Reference Manual, for
details.
ZSH_VERSION
is set. By default @command{zsh} is not
compatible with the Bourne shell: you have to run `emulate sh' and
set NULLCMD
to `:'. See section `Compatibility' in The Z Shell Manual, for details.
Zsh 3.0.8 is the native @command{/bin/sh} on Mac OS X 10.0.3.
The following discussion between Russ Allbery and Robert Lipe is worth reading:
Russ Allbery:
The GNU assumption that @command{/bin/sh} is the one and only shell leads to a permanent deadlock. Vendors don't want to break user's existant shell scripts, and there are some corner cases in the Bourne shell that are not completely compatible with a POSIX shell. Thus, vendors who have taken this route will never (OK..."never say never") replace the Bourne shell (as @command{/bin/sh}) with a POSIX shell.
Robert Lipe:
This is exactly the problem. While most (at least most System V's) do have a bourne shell that accepts shell functions most vendor @command{/bin/sh} programs are not the POSIX shell.
So while most modern systems do have a shell _somewhere_ that meets the POSIX standard, the challenge is to find it.
Don't rely on `\' being preserved just because it has no special meaning together with the next symbol. in the native @command{/bin/sh} on OpenBSD 2.7 `\"' expands to `"' in here-documents with unquoted delimiter. As a general rule, if `\\' expands to `\' use `\\' to get `\'.
With OpenBSD 2.7's @command{/bin/sh}
$ cat <<EOF > \" \\ > EOF " \
and with Bash:
bash-2.04$ cat <<EOF > \" \\ > EOF \" \
Many older shells (including the Bourne shell) implement here-documents inefficiently. Users can generally speed things up by using a faster shell, e.g., by using the command `bash ./configure' rather than plain `./configure'.
Some shells can be extremely inefficient when there are a lot of here-documents inside a single statement. For instance if your `configure.ac' includes something like:
if <cross_compiling>; then assume this and that else check this check that check something else ... on and on forever ... fi
A shell parses the whole if
/fi
construct, creating
temporary files for each here document in it. Some shells create links
for such here-documents on every fork
, so that the clean-up code
they had installed correctly removes them. It is creating the links
that the shell can take forever.
Moving the tests out of the if
/fi
, or creating multiple
if
/fi
constructs, would improve the performance
significantly. Anyway, this kind of construct is not exactly the
typical use of Autoconf. In fact, it's even not recommended, because M4
macros can't look into shell conditionals, so we may fail to expand a
macro when it was expanded before in a conditional path, and the
condition turned out to be false at run-time, and we end up not
executing the macro at all.
Some file descriptors shall not be used, since some systems, admittedly arcane, use them for special purpose:
Don't redirect several times the same file descriptor, as you are doomed to failure under Ultrix.
ULTRIX V4.4 (Rev. 69) System #31: Thu Aug 10 19:42:23 GMT 1995 UWS V4.4 (Rev. 11) $ eval 'echo matter >fullness' >void illegal io $ eval '(echo matter >fullness)' >void illegal io $ (eval '(echo matter >fullness)') >void Ambiguous output redirect.
In each case the expected result is of course `fullness' containing `matter' and `void' being empty.
Don't try to redirect the standard error of a command substitution: it must be done inside the command substitution: when running `: `cd /zorglub` 2>/dev/null' expect the error message to escape, while `: `cd /zorglub 2>/dev/null`' works properly.
It is worth noting that Zsh (but not Ash nor Bash) makes it possible in assignments though: `foo=`cd /zorglub` 2>/dev/null'.
Most shells, if not all (including Bash, Zsh, Ash), output traces on stderr, even for sub-shells. This might result in undesired content if you meant to capture the standard-error output of the inner command:
$ ash -x -c '(eval "echo foo >&2") 2>stderr' $ cat stderr + eval echo foo >&2 + echo foo foo $ bash -x -c '(eval "echo foo >&2") 2>stderr' $ cat stderr + eval 'echo foo >&2' ++ echo foo foo $ zsh -x -c '(eval "echo foo >&2") 2>stderr' # Traces on startup files deleted here. $ cat stderr +zsh:1> eval echo foo >&2 +zsh:1> echo foo foo
You'll appreciate the various levels of detail...
One workaround is to grep out uninteresting lines, hoping not to remove good ones...
While @command{autoconf} and friends will usually be run on some Unix variety, it can and will be used on other systems, most notably DOS variants. This impacts several assumptions regarding file and path names.
For example, the following code:
case $foo_dir in /*) # Absolute ;; *) foo_dir=$dots$foo_dir ;; esac
will fail to properly detect absolute paths on those systems, because they can use a drivespec, and will usually use a backslash as directory separator. The canonical way to check for absolute paths is:
case $foo_dir in [\\/]* | ?:[\\/]* ) # Absolute ;; *) foo_dir=$dots$foo_dir ;; esac
Make sure you quote the brackets if appropriate and keep the backslash as first character (see section Limitations of Shell Builtins).
Also, because the colon is used as part of a drivespec, these systems don't
use it as path separator. When creating or accessing paths, use
$ac_path_separator
instead (or the PATH_SEPARATOR
output
variable). @command{autoconf} sets this to the appropriate value (`:'
or `;') when it starts up.
File names need extra care as well. While DOS-based environments that are Unixy enough to run @command{autoconf} (such as DJGPP) will usually be able to handle long file names properly, there are still limitations that can seriously break packages. Several of these issues can be easily detected by the @href{ftp://ftp.gnu.org/gnu/non-gnu/doschk/doschk-1.1.tar.gz, doschk} package.
A short overview follows; problems are marked with SFN/LFN to indicate where they apply: SFN means the issues are only relevant to plain DOS, not to DOS boxes under Windows, while LFN identifies problems that exist even under Windows.
AC_CONFIG_HEADER(config.h) AC_CONFIG_FILES([source.c foo.bar]) AC_OUTPUTbut it causes problems on DOS, as it requires `config.h.in', `source.c.in' and `foo.bar.in'. To make your package more portable to DOS-based environments, you should use this instead:
AC_CONFIG_HEADER(config.h:config.hin) AC_CONFIG_FILES([source.c:source.cin foo.bar:foobar.in]) AC_OUTPUT
Contrary to a persistent urban legend, the Bourne shell does not
systematically split variables and backquoted expressions, in particular
on the right-hand side of assignments and in the argument of case
.
For instance, the following code:
case "$given_srcdir" in .) top_srcdir="`echo "$dots" | sed 's,/$,,'`" *) top_srcdir="$dots$given_srcdir" ;; esac
is more readable when written as:
case $given_srcdir in .) top_srcdir=`echo "$dots" | sed 's,/$,,'` *) top_srcdir=$dots$given_srcdir ;; esac
and in fact it is even more portable: in the first case of the
first attempt, the computation of top_srcdir
is not portable,
since not all shells properly understand "`..."..."...`"
.
Worse yet, not all shells understand "`...\"...\"...`"
the same way. There is just no portable way to use double-quoted
strings inside double-quoted backquoted expressions (pfew!).
$@
${var:-value}
sh
, don't accept the
colon for any shell substitution, and complain and die.
${var=literal}
: ${var='Some words'}otherwise some shells, such as on Digital Unix V 5.0, will die because of a "bad substitution". Solaris' @command{/bin/sh} has a frightening bug in its interpretation of this. Imagine you need set a variable to a string containing `}'. This `}' character confuses Solaris' @command{/bin/sh} when the affected variable was already set. This bug can be exercised by running:
$ unset foo $ foo=${foo='}'} $ echo $foo } $ foo=${foo='}' # no error; this hints to what the bug is $ echo $foo } $ foo=${foo='}'} $ echo $foo }} ^ ugh!It seems that `}' is interpreted as matching `${', even though it is enclosed in single quotes. The problem doesn't happen using double quotes.
${var=expanded-value}
default="yu,yaa" : ${var="$default"}will set var to `M-yM-uM-,M-yM-aM-a', i.e., the 8th bit of each char will be set. You won't observe the phenomenon using a simple `echo $var' since apparently the shell resets the 8th bit when it expands $var. Here are two means to make this shell confess its sins:
$ cat -v <<EOF $var EOFand
$ set | grep '^var=' | cat -vOne classic incarnation of this bug is:
default="a b c" : ${list="$default"} for c in $list; do echo $c doneYou'll get `a b c' on a single line. Why? Because there are no spaces in `$list': there are `M- ', i.e., spaces with the 8th bit set, hence no IFS splitting is performed!!! One piece of good news is that Ultrix works fine with `: ${list=$default}'; i.e., if you don't quote. The bad news is then that QNX 4.25 then sets list to the last item of default! The portable way out consists in using a double assignment, to switch the 8th bit twice on Ultrix:
list=${list="$default"}...but beware of the `}' bug from Solaris (see above). For safety, use:
test "${var+set}" = set || var={value}
`commands`
$ pwd /tmp $ test -n "`cd /`" && pwd /The result of `foo=`exit 1`' is left as an exercise to the reader.
$(commands)
$ showrev -c /bin/sh | grep version Command version: SunOS 5.8 Generic 109324-02 February 2001 $ echo $(echo blah) syntax error: `(' unexpectednor does IRIX 6.5's Bourne shell:
$ uname -a IRIX firebird-image 6.5 07151432 IP22 $ echo $(echo blah) $(echo blah)
When setting several variables in a row, be aware that the order of the evaluation is undefined. For instance `foo=1 foo=2; echo $foo' gives `1' with sh on Solaris, but `2' with Bash. You must use `;' to enforce the order: `foo=1; foo=2; echo $foo'.
Don't rely on the exit status of an assignment: Ash 0.2 does not change the status and propagates that of the last statement:
$ false || foo=bar; echo $? 1 $ false || foo=`:`; echo $? 0
and to make things even worse, QNX 4.25 just sets the exit status to 0 in any case:
$ foo=`exit 1`; echo $? 0
To assign default values, follow this algorithm:
: ${var='my literal'}
: ${var="$default"}
var=${var="$default"}
test "${var+set}" = set || var='${indirection}'
In most cases `var=${var="$default"}' is fine, but in case of doubt, just use the latter. See section Shell Substitutions, items `${var:-value}' and `${var=value}' for the rationale.
Some shell variables should not be used, since they can have a deep influence on the behavior of the shell. In order to recover a sane behavior from the shell, some variables should be unset, but @command{unset} is not portable (see section Limitations of Shell Builtins) and a fallback value is needed. We list these values below.
CDPATH
cd
is verbose, so idioms such as
`abs=`cd $rel && pwd`' break because abs
receives the path
twice.
Setting CDPATH
to the empty value is not enough for most shells.
A simple colon is enough except for zsh
, which prefers a leading
dot:
zsh-3.1.6 % mkdir foo && (CDPATH=: cd foo) /tmp/foo zsh-3.1.6 % (CDPATH=:. cd foo) /tmp/foo zsh-3.1.6 % (CDPATH=.: cd foo) zsh-3.1.6 %(of course we could just
unset
CDPATH
, since it also
behaves properly if set to the empty string).
Life wouldn't be so much fun if @command{bash} and @command{zsh} had the
same behavior:
bash-2.02 % (CDPATH=:. cd foo) bash-2.02 % (CDPATH=.: cd foo) /tmp/fooTherefore, a portable solution to neutralize `CDPATH' is
CDPATH=${ZSH_VERSION+.}:Note that since @command{zsh} supports @command{unset}, you may unset `CDPATH' using `:' as a fallback, see section Limitations of Shell Builtins.
IFS
IFS
to backslash. Indeed,
Bourne shells use the first character (backslash) when joining the
components in `"$@"' and some shells then re-interpret (!) the
backslash escapes, so you can end up with backspace and other strange
characters.
LANG
LC_ALL
LC_TIME
LC_CTYPE
LANGUAGE
LC_COLLATE
LC_NUMERIC
LC_MESSAGES
set
! Non-C
@env{LC_CTYPE} values break the ctype check. Fixing @env{LC_COLLATE}
makes scripts more portable in some cases. For example, it causes the
regular expression `[a-z]' to match only lower-case letters on
ASCII platforms. However, `[a-z]' does not work in general
even when @env{LC_COLLATE} is fixed; for example, it does not work for
EBCDIC platforms. For maximum portability, you should use regular
expressions like `[abcdefghijklmnopqrstuvwxyz]' that list
characters explicitly instead of relying on ranges.
If one of these variables is set, you should try to unset it,
using `C' as a fall back value. see section Limitations of Shell Builtins,
builtin @command{unset}, for more details.
NULLCMD
NULLCMD
is
`:', while @command{zsh}, even in Bourne shell compatibility mode,
sets NULLCMD
to `cat'. If you forgot to set NULLCMD
,
your script might be suspended waiting for data on its standard input.
status
zsh
(at least 3.1.6),
hence read-only. Do not use it.
PATH_SEPARATOR
PATH_SEPARATOR
variable can be set to
either `:' or `;' to control the path separator @command{bash}
uses to set up certain environment variables (such as
PATH
). Since this only works inside bash, you want autoconf to
detect the regular DOS path separator `;', so it can be safely
substituted in files that may not support `;' as path separator. So
either unset this variable or set it to `;'.
RANDOM
RANDOM
, a variable that returns a different
integer when used. Most of the time, its value does not change when it
is not used, but on IRIX 6.5 the value changes all the time. This
can be observed by using @command{set}.
No, no, we are serious: some shells do have limitations! :)
You should always keep in mind that any built-in or command may support
options, and therefore have a very different behavior with arguments
starting with a dash. For instance, the innocent `echo "$word"'
can give unexpected results when word
starts with a dash. It is
often possible to avoid this problem using `echo "x$word"', taking
the `x' into account later in the pipe.
fnmatch
, @command{bash} fails to properly
handle backslashes in character classes:
bash-2.02$ case /tmp in [/\\]*) echo OK;; esac bash-2.02$This is extremely unfortunate, since you are likely to use this code to handle UNIX or MS-DOS absolute paths. To work around this bug, always put the backslash first:
bash-2.02$ case '\TMP' in [\\/]*) echo OK;; esac OK bash-2.02$ case /tmp in [\\/]*) echo OK;; esac OK
echo
is probably the most surprising source of
portability troubles. It is not possible to use `echo' portably
unless both options and escape sequences are omitted. New applications
which are not aiming at portability should use `printf' instead of
`echo'.
Don't expect any option. See section Preset Output Variables, ECHO_N
etc. for a means to simulate @option{-c}.
Do not use backslashes in the arguments, as there is no consensus on
their handling. On `echo '\n' | wc -l', the @command{sh} of
Digital Unix 4.0, MIPS RISC/OS 4.52, answer 2, but the Solaris'
@command{sh}, Bash and Zsh (in @command{sh} emulation mode) report 1.
Please note that the problem is truly @command{echo}: all the shells
understand `'\n'' as the string composed of a backslash and an
`n'.
Because of these problems, do not pass a string containing arbitrary
characters to @command{echo}. For example, `echo "$foo"' is safe
if you know that foo's value cannot contain backslashes and cannot
start with `-', but otherwise you should use a here-document like
this:
cat <<EOF $foo EOF
$?
;
unfortunately, some shells, such as the DJGPP port of Bash 2.04, just
perform `exit 0'.
bash-2.04$ foo=`exit 1` || echo fail fail bash-2.04$ foo=`(exit 1)` || echo fail fail bash-2.04$ foo=`(exit 1); exit` || echo fail bash-2.04$Using `exit $?' restores the expected behavior. Some shell scripts, such as those generated by @command{autoconf}, use a trap to clean up before exiting. If the last shell command exited with nonzero status, the trap also exits with nonzero status so that the invoker can tell that an error occurred. Unfortunately, in some shells, such as Solaris 8 @command{sh}, an exit trap ignores the
exit
command's status. In these shells, a trap
cannot determine whether it was invoked by plain exit
or by
exit 1
. Instead of calling exit
directly, use the
AC_MSG_ERROR
macro that has a workaround for this problem.
#! /bin/sh echo $FOO FOO=bar echo $FOO exec /bin/sh $0when run with `FOO=foo' in the environment, these shells will print alternately `foo' and `bar', although it should only print `foo' and then a sequence of `bar's. Therefore you should @command{export} again each environment variable that you update.
for arg do echo "$arg" doneYou may not leave the
do
on the same line as for
,
since some shells improperly grok:
for arg; do echo "$arg" doneIf you want to explicitly refer to the positional arguments, given the `$@' bug (see section Shell Substitutions), use:
for arg in ${1+"$@"}; do echo "$arg" done
if ! cmp -s file file.new; then mv file.new file fiuse:
if cmp -s file file.new; then :; else mv file.new file fiThere are shells that do not reset the exit status from an @command{if}:
$ if (exit 42); then true; fi; echo $? 42whereas a proper shell should have printed `0'. This is especially bad in Makefiles since it produces false failures. This is why properly written Makefiles, such as Automake's, have such hairy constructs:
if test -f "$file"; then install "$file" "$dest" else : fi
set x $my_list; shift
test
program is the way to perform many file and string
tests. It is often invoked by the alternate name `[', but using
that name in Autoconf code is asking for trouble since it is an M4 quote
character.
If you need to make multiple checks using test
, combine them with
the shell operators `&&' and `||' instead of using the
test
operators @option{-a} and @option{-o}. On System V, the
precedence of @option{-a} and @option{-o} is wrong relative to the unary
operators; consequently, POSIX does not specify them, so using them
is nonportable. If you combine `&&' and `||' in the same
statement, keep in mind that they have equal precedence.
You may use `!' with @command{test}, but not with @command{if}:
`test ! -r foo || exit 1'.
configure
scripts to support cross-compilation, they
shouldn't do anything that tests features of the build system instead of
the host system. But occasionally you may find it necessary to check
whether some arbitrary file exists. To do so, use `test -f' or
`test -r'. Do not use `test -x', because 4.3BSD does not
have it. Do not use `test -e' either, because Solaris 2.5 does not
have it.
test
might interpret its argument as an
option (e.g., `string = "-n"').
Contrary to a common belief, `test -n string' and `test
-z string' are portable, nevertheless many shells (such
as Solaris 2.5, AIX 3.2, UNICOS 10.0.0.6, Digital Unix 4 etc.) have
bizarre precedence and may be confused if string looks like an
operator:
$ test -n = test: argument expectedIf there are risks, use `test "xstring" = x' or `test "xstring" != x' instead. It is frequent to find variations of the following idiom:
test -n "`echo $ac_feature | sed 's/[-a-zA-Z0-9_]//g'`" && actionto take an action when a token matches a given pattern. Such constructs should always be avoided by using:
echo "$ac_feature" | grep '[^-a-zA-Z0-9_]' >/dev/null 2>&1 && actionUse
case
where possible since it is faster, being a shell builtin:
case $ac_feature in *[!-a-zA-Z0-9_]*) action;; esacAlas, negated character classes are probably not portable, although no shell is known to not support the POSIX.2 syntax `[!...]' (when in interactive mode, @command{zsh} is confused by the `[!...]' syntax and looks for an event in its history because of `!'). Many shells do not support the alternative syntax `[^...]' (Solaris, Digital Unix, etc.). One solution can be:
expr "$ac_feature" : '.*[^-a-zA-Z0-9_]' >/dev/null && actionor better yet
expr "x$ac_feature" : '.*[^-a-zA-Z0-9_]' >/dev/null && action`expr "Xfoo" : "Xbar"' is more robust than `echo "Xfoo" | grep "^Xbar"', because it avoids problems when `foo' contains backslashes.
$ cat trap.sh trap 'echo $?' 0 (exit 42); exit 0 $ zsh trap.sh 42 $ bash trap.sh 0The portable solution is then simple: when you want to `exit 42', run `(exit 42); exit 42', the first @command{exit} being used to set the exit status to 42 for Zsh, and the second to trigger the trap and pass 42 as exit status for Bash. The shell in FreeBSD 4.0 has the following bug: `$?' is reset to 0 by empty lines if the code is inside @command{trap}.
$ trap 'false echo $?' 0 $ exit 0Fortunately, this bug only affects @command{trap}.
In a sense, yes, because if it doesn't exist, the shell will produce an exit status of failure, which is correct for @command{false}, but not for @command{true}.
CDPATH
or LANG
, you can test for its existence and use
it provided you give a neutralizing value when @command{unset} is
not supported:
if (unset FOO) >/dev/null 2>&1; then unset=unset else unset=false fi $unset CDPATH || CDPATH=:See section Special Shell Variables, for some neutralizing values. Also, see section Limitations of Shell Builtins, documentation of @command{export}, for the case of environment variables.
The small set of tools you can expect to find on any machine can still include some limitations you should be aware of.
$ gawk 'function die () { print "Aaaaarg!" } BEGIN { die () }' gawk: cmd. line:2: BEGIN { die () } gawk: cmd. line:2: ^ parse error $ gawk 'function die () { print "Aaaaarg!" } BEGIN { die() }' Aaaaarg!If you want your program to be deterministic, don't depend on
for
on arrays:
$ cat for.awk END { arr["foo"] = 1 arr["bar"] = 1 for (i in arr) print i } $ gawk -f for.awk </dev/null foo bar $ nawk -f for.awk </dev/null bar fooSome AWK, such as HPUX 11.0's native one, have regex engines fragile to inner anchors:
$ echo xfoo | $AWK '/foo|^bar/ { print }' $ echo bar | $AWK '/foo|^bar/ { print }' bar $ echo xfoo | $AWK '/^bar|foo/ { print }' xfoo $ echo bar | $AWK '/^bar|foo/ { print }' barEither do not depend on such patterns (i.e., use `/^(.*foo|bar)/', or use a simple test to reject such AWK.
link
(or, in
newer systems, rename
).
dir=`expr "x$file" : 'x\(.*\)/[^/]*' \| '.' : '.'But there are a few subtilities, e.g., under UN*X, should `//1' give `/'? Paul Eggert answers:
No, under some older flavors of Unix, leading `//' is a special path name: it refers to a "super-root" and is used to access other machines' files. Leading `///', `////', etc. are equivalent to `/'; but leading `//' is special. I think this tradition started with Apollo Domain/OS, an OS that is still in use on some older hosts.
POSIX.2 allows but does not require the special treatment for `//'. It says that the behavior of dirname on path names of the form `//([^/]+/*)?' is implementation defined. In these cases, GNU @command{dirname} returns `/', but it's more portable to return `//' as this works even on those older flavors of Unix.
I have heard rumors that this special treatment of `//' may be dropped in future versions of POSIX, but for now it's still the standard.
> printf "foo\n|foo\n" | egrep '^(|foo|bar)$' |foo > printf "bar\nbar|\n" | egrep '^(foo|bar|)$' bar| > printf "foo\nfoo|\n|bar\nbar\n" | egrep '^(foo||bar)$' foo |bar@command{egrep} also suffers the limitations of @command{grep}.
length
, substr
, match
and index
.
expr " \| "GNU/Linux and POSIX.2-1992 return the empty string for this case, but traditional Unix returns `0' (Solaris is one such example). In the latest POSIX draft, the specification has been changed to match traditional Unix's behavior (which is bizarre, but it's too late to fix this). Please note that the same problem does arise when the empty string results from a computation, as in:
expr bar : foo \| foo : barAvoid this portability problem by avoiding the empty string.
expr a : b \| "unfortunately this behaves exactly as the original expression, see the `@command{expr' (`:')} entry for more information. Older @command{expr} implementations (e.g. SunOS 4 @command{expr} and Solaris 8 @command{/usr/ucb/expr}) have a silly length limit that causes @command{expr} to fail if the matched substring is longer than 120 bytes. In this case, you might want to fall back on `echo|sed' if @command{expr} fails. Don't leave, there is some more! The QNX 4.25 @command{expr}, in addition of preferring `0' to the empty string, has a funny behavior in its exit status: it's always 1 when parentheses are used!
$ val=`expr 'a' : 'a'`; echo "$?: $val" 0: 1 $ val=`expr 'a' : 'b'`; echo "$?: $val" 1: 0 $ val=`expr 'a' : '\(a\)'`; echo "?: $val" 1: a $ val=`expr 'a' : '\(b\)'`; echo "?: $val" 1: 0In practice this can be a big problem if you are ready to catch failures of @command{expr} programs with some other method (such as using @command{sed}), since you may get twice the result. For instance
$ expr 'a' : '\(a\)' || echo 'a' | sed 's/^\(a\)$/\1/'will output `a' on most hosts, but `aa' on QNX 4.25. A simple work around consists in testing @command{expr} and use a variable set to @command{expr} or to @command{false} according to the result.
grep
to `/dev/null'. Check the exit
status of grep
to determine whether it found a match.
Don't use multiple regexps with @option{-e}, as some grep
will only
honor the last pattern (eg., IRIX 6.5 and Solaris 2.5.1). Anyway,
Stardent Vistra SVR4 grep
lacks @option{-e}... Instead, use
alternation and egrep
.
$ echo a | sed 's/x/x/;;s/x/x/' sed: 1: "s/x/x/;;s/x/x/": invalid command code ;Input should have reasonably long lines, since some @command{sed} have an input buffer limited to 4000 bytes. Alternation, `\|', is common but not portable. Anchors (`^' and `$') inside groups are not portable. Nested groups are extremely portable, but there is at least one @command{sed} (System V/68 Base Operating System R3V7.1) that does not support it. Of course the option @option{-e} is portable, but it is not needed. No valid Sed program can start with a dash, so it does not help disambiguating. Its sole usefulness is helping enforcing indenting as in:
sed -e instruction-1 \ -e instruction-2as opposed to
sed instruction-1;instruction-2Contrary to yet another urban legend, you may portably use `&' in the replacement part of the
s
command to mean "what was
matched".
s/keep me/kept/g # a t end # b s/.*/deleted/g # c : end # don
delete me # 1 delete me # 2 keep me # 3 delete me # 4you get
deleted delete me kept deletedinstead of
deleted deleted kept deletedWhy? When processing 1, a matches, therefore sets the t flag, b jumps to d, and the output is produced. When processing line 2, the t flag is still set (this is the bug). Line a fails to match, but @command{sed} is not supposed to clear the t flag when a substitution fails. Line b sees that the flag is set, therefore it clears it, and jumps to d, hence you get `delete me' instead of `deleted'. When processing 3 t is clear, a matches, so the flag is set, hence b clears the flags and jumps. Finally, since the flag is clear, 4 is processed properly. There are two things one should remind about `t' in @command{sed}. Firstly, always remember that `t' jumps if some substitution succeeded, not only the immediately preceding substitution, therefore, always use a fake `t clear; : clear' to reset the t flag where indeed. Secondly, you cannot rely on @command{sed} to clear the flag at each new cycle. One portable implementation of the script above is:
t clear : clear s/keep me/kept/g t end s/.*/deleted/g : end
echo
as a workaround.
GNU @command{touch} 3.16r (and presumably all before that) fails to work
on SunOS 4.1.3 when the empty file is on an NFS-mounted 4.2 volume.
Make itself suffers a great number of limitations, only a few of which being listed here. First of all, remember that since commands are executed by the shell, all its weaknesses are inherited...
$ cat Makefile _am_include = # _am_quote = all:; @echo this is test % make Make: Must be a separator on rules line 2. Stop. $ cat Makefile2 am_include = # am_quote = all:; @echo this is test $ make -f Makefile2 this is test
VPATH
VPATH
causes Sun
@command{make} to only execute the first set of double-colon rules.
Go to the first, previous, next, last section, table of contents.