ARG_MAX
| Shells
| portability
| permissions
| UUOC
| ancient
| -
| ../Various
| HOME
$() vs )
| IFS
| using siginfo
| nanosleep
| line charset
| locale
distinguishing them from bourne compatible shells
The purpose of this page is not to point out portability problems (this is a side effect) but to identify characteristic features; probably just for fun. Otherwise this page also would have to list what the bourne shell might share with one or a few other shells only. Instead, if you're interested in portability: Paul Jarc tries to document all the suspicious or nonportable constructs that can impact portable script-writing.
Content:
Common to all versions
Common to all versions since SVR1
Common to all versions since SVR2 - related to functions
Common to all versions since SVR2 - related to the unset built-in
Common to all versions since SVR2 - related to the hash built-in
Common to all versions since SVR2 - other issues
Not exclusive to traditional Bourne shells (wrongly might be considered characteristical)
$()
export VAR=value" isn't accepted
(but "VAR=value export VAR" is accepted and portable)
However, this feature is not documented in all manuals
(e.g., not for Version 7, the BSDs, later Ultrix sh, System III,
AIX 3 ff., IRIX 3 ff., Reliant and Solaris).
Some manuals which don't document this alternative,
also wrongly don't list it under having "a special meaning to the shell"
("Quoting"),
e.g., Version 7, the BSDs, System III, AIX 3 ff. and Reliant --
although '^' is actually useful in practice for,
e.g., "stty(1)".
On System III, /etc/wall is a script and it contains a line like the following,
interestingly even with both ^ and |:
who^sed -e '[..build a commandline..]' | sh
while", "for" and "if",
but also "{ list;}",
causes a sub-shell to be created. The main impacts are:
the isolated environment (i.e., variable assignments) and
"exit" does not leave the script but only the
redirected structure.
Workaround for variable assignments (example taken from the Heirloom shell package):
exec 5<&0 <input
while read line: do
...
variable=value
...
done
exec <&5 5<&\-
(The variable in a redirected
"while read variable"
construct gets assigned the empty value when reading EOF,
i.e., if you don't break out before that.
This may appear as if the variable was localized even in
modern shells.)
set lists the local value in case (whereas
env only can list exported values).
There is an exception to this rule: If you call an
executable bourne shell script directly from a bourne shell
("./script" instead of "sh ./script"),
then the shell optimizes subsequent forks and the change of the
variable becomes global:
$ VAR=global export VAR
$ cat script
VAR=local
env|grep VAR
$ sh script
VAR=exported
$ ./script
VAR=local
IFS is even used to split (unquoted) words, thus
''IFS=X; echoXfoo'' calls echo with
the argument foo.
$9 are not
directly accessible, but only via "shift".
>>" redirection, open(2)
is not called with O_APPEND, but instead the shell jumps
to the end with lseek(2). Going on with writing after a
truncation (e.g., cutting a logfile) will lead to a sparse whole of
the original size, i.e., then you'll read NULL bytes at the beginning.
(A few other shells, like csh(1) and early ksh(1),
(but not modern bourne compatibles) behave similar. The reason for
all this (pointed out in <3DAF47B9.PR1RJ8L6@bigfoot.de>) is
that O_APPEND had not been introduced until
System III and 4.2BSD ("4.1c.2").)
Some examples are
cd nonexistent || echo error
readonly var; unset var
. non-accessible-file
test x -unknownoperator y
read (without any arguments)
However, these are not fatal:
exec 0<&-; read var (reading with a closed STDIN)
kill (without any arguments)
An appropriate workaround for the cd example is:
(cd nonexistent) && cd nonexistent || echo error
You can also see this in an interactive shell if you type several commands separated by ;
the execution of the command line is aborted immediately.
nonex > file" doesn't truncate the file,
whereas "/nonex > file" does so.
command like cat, it fails with built-ins
like "read a" or ":". You
get "/tmp/sh12345: cannot open", except on OpenServer5.x
where this has been fixed.
command<<EOF
`pwd`
EOF
command<<EOF &
`pwd`
EOF
IFS= read variable".
(POSIX also knows this behaviour, but here it applies only to special built-ins. Examples are ksh88/93, bash in posix mode, pdksh.)
echo x 1>&3).
[spotted in the autoconf documentation]
${var=literal value},
i.e., a literal value with blanks but not surrounded by quotes.
Workaround: use a variable instead:
tmp="literal value'; echo ${var=$tmp},
or quote the value like
${var="literal value"}
or ${var=literal\ value}
set --",
but only by shifting away the parameters
(or shorter: set dummy; shift).
for i do" (omitting the optional
"in word") is a portable way to iterate over all
arguments.for i; do" is not accepted in traditional
Bourne shells, although you interestingly may insert a newline instead
of the semicolon. (This is usually only possible if a semicolon
was accepted also.)
. script <arguments>"
are discarded.
A workaround exploits the fact that the positional parameters are delivered instead:
my_function() {
. script
}
my_function <arguments>
echo `var=old var=new env` prints var=old)
trap SIGSEGV,
because this signal is usually used for the internal memory management.
However, as it doesn't work this way on the 68K architecture, memory management is implemented differently on SunOS 3.1 (and ff.) for example. (And trapping SIGSEGV was allowed then on SunOS 3 and 4.)
See more about this memory management at the bottom of the main page.
echo a > file1 b > file2" isn't evaluated
correctly. "a b" will end in file1, although it should be
in file2. It works correctly if not inserting anything
between the redirections: "echo a b > file1 > file2".
2>&1 program >/dev/null" isn't evaluated
correctly: the order is reversed and thus stderr is also redirected.
sh -vx" instead of "sh -v -x".
case x in in)" fails with
"syntax error at line x: `in' unexpected"
(exception: fixed on SunOS 5.x).
(But note that some other, special combinations are even accepted
by ksh88 and ksh93, too:
echo "`pwd"
echo `'pwd`
echo `"pwd`)
As exception, this is fixed on SunOS since 5.5.
var=1 function") are without effect,
that is, the variable remains unchanged both in the function
and in the current environment.
my_function() {
cat<<EOF
$@
EOF
}
This will already create a temporary file by the time this definition.
As illustration: "type my_function" then will result in something like
my_function is a function
my_function(){
cat 0<</tmp/sh123450
}
with /tmp/sh123450 containing the body of
the here-document, $@.
Two notes in advance:
The problem, however: the function is visible in called scripts if they are created as subshell.
This happens for example if you call an executable shell script (without the #! mechanism).
script: /tmp/sh123456: cannot open
(A workaround for all this is to put the function body into "eval '...'")
unset" built-in:IFS PATH PS1 PS2 MAILCHECK.
As exception, bash-1.x doesn't allow this for all
(but MAILCHECK), too
hash" built-in: With more elements, commands are not found by path search anymore.
A possible error message is "unknown builtin".
The bug does not trigger if the command is called for the first time;
but if you call "hash -r" in advance, it does.
For the following code there's a race condition:
the right command of the pipeline (grep) might be starting before
the temporary file containing the here-document was created.
The shell then errors with a "/tmp/sh12345: cannot open"
echo `cat <<EOF|grep x
x
EOF
`
If the here-document is also right part of a pipeline at the same time,
then the probability to trigger the race was highly increased for me:
echo `:|cat <<EOF|grep x
x
EOF
`
As exception, this was fixed on SunOS 5.5.
Using here-documents in functions will create a temporary file at the time of definition already (see above).
(A workaround is to put the function body into "eval '...'")
If such a function from above is executed in the background and the skript itself then exits immediately, an error is displayed instead:
$ cat f
my_function() {
cat<<EOF
test
EOF
}
(sleep 1; my_function) &
$ sh f
f: /tmp/sh123456: cannot open
The function definition (containing the temp file) is exported to a subshell.
However the parent (exiting before the subshell),
wrongly cleans up the temp file.
(Workaround again: put the function body into "eval '...'")
echo x >''
echo x > $unset_or_empty_variable
ksh93-l' just ignore these redirections, too.
echo "`pwd"
IFS=''; echo "$*"
echo x > a*
test -n $variable" if variable is set to
"=" (also keep in mind the values "-" and "!").
test" is 1 ("false") (instead of 2, "error")
var=pre; var=post echo $var > $var" creates a file named post (containing pre).
test" built-in operators -a and -o check for both conditions,
even if stopping after the first would be sufficient.
case x in (x)" is not accepted.
$(...)" form of command substitution.
2009-08-19, <http://www.in-ulm.de/~mascheck/bourne/common.html>