9045
Comment: Dash has -nt / -ot / -ef tests.
|
380
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
= How to make bash scripts work in dash = This page is an attempt to list some of the most common bashisms, i.e. features not defined by POSIX (won't work in dash, or general `/bin/sh`). It probably won't be exhaustive. Note also we talk about "bashism" because this wiki is largely bash-centric but a number of these extensions work in other shells like ksh or zsh. == Syntax == || ||'''Works in bash''' ||'''Change to for dash''' ||'''Comment''' || ||defining functions ||function f { echo hello world; } ||f() { echo hello world; } ||"function" is not defined by POSIX, only "name ()" is. In ksh both forms are present, but functions defined with "function" work slightly differently. || ||case ||`;;&` `;&` etc ||None. Duplicate the case (use a function to avoid code duplication) ||`;;&` `;&` in bash4 is not defined by POSIX. ksh93 has `;&` but not ;;&` || ||numeric C-like for loop ||`for ((i=0; i<3; i++)); do`<<BR>>` echo $i`<<BR>>`done` ||`i=0 ; while test $i -lt 3 ; do`<<BR>>` echo $i ; i=$(($i+1))`<<BR>>`done` ||this syntax is not defined by POSIX. Present in ksh93. || ||expand sequences ||`echo $'hello\tworld'` ||`printf "hello\tworld\n"` ||`$' '` is not defined by POSIX and is bash-specific || ||extended glob ||`+( ) @( ) !( ) *( )` ||not always possible, sometimes you can use several globs, sometimes you can use find(1) ||not defined by POSIX. Present in ksh. || ||select ||`select` ||some ideas: implement the menu yourself, use a command like dialog ||not defined by POSIX. Present in ksh. || == Expansions == * Brace Expansion, eg `{a,b,c}` or `{1..10}` is not defined by POSIX. Both forms are present in ksh93, and the first form in older ksh. * The `<( )` and `>( )` process substitutions are not defined by POSIX, but can be simulated with FIFOs: instead of `foo <(bar)`, write `mkfifo fifo; bar > fifo & foo fifo` (this is basically how process substitution is implemented on OSes that don't have a mechanism like `/dev/fd/` to refer to unnamed pipes with filenames). It is present in ksh93. == Parameter Expansions == List of expansions not defined by POSIX: * `${name:n:l}` -- if the variable contains no newlines, you can use `$(printf '%s\n' "$name" | cut -c $n-$((n+l-1)))`. If there are newlines, you can use `$(printf %s "$name" | dd bs=1 skip=$n count=$l 2>/dev/null)`, but printf may report a (harmless) broken pipe if the variable contents are large enough. This form is present in ksh93. * `${name/ }` -- you can use `$(printf '%s\n' "$name" | sed 's/foo/bar/')`, after changing shell patterns to regular expressions. This form is present in ksh93. * `${!name}` -- bash-specific; it is possible, but dangerous, to use `eval` to achieve similar effects; see [[BashFAQ/006]]. Note that using `$( )` has the side-effect of removing trailing newlines from the results. Furthermore, since many standard Posix utilities, such as `sed`, require text files as input, you should ensure their input ends in a newline. == Arrays == [[BashFAQ/005|Arrays]] are not defined by POSIX (but are present in ksh); there is no easy general workaround for arrays. Here are some hints: * The positional parameters are a kind of array (only one array): {{{ #build a command dynamically see [[BashFAQ/050]] set -- 'mycommand' 'needs some complex' 'args' "$@" #access the i'th param set -- one two three i=2 eval var=\$$i #take care if i comes from some user input see below echo "$var" }}} * use `IFS` and `set -f` * `eval` is powerful but dangerous so use it wisely. See [[BashFAQ/048|Eval command and security issues]]. == Conditionals == || ||'''Works in bash''' ||'''Change to for dash''' ||'''Comment''' || ||simple test ||`[[` ||use [ and use double quotes around the expansions `[ "$var" = "" ]` ||[[ is not defined by POSIX, but is present in ksh || ||pattern matching ||`[[ foo = *glov ]]` ||use `case` or `grep` ||see [[BashFAQ/041]] || ||equality with test ||`==` ||use `=` instead ||only `=` is defined by POSIX, `=` works also in bash || ||compare lexicographically. ||`< >` ||no change ||present in dash and ksh, but not defined by POSIX. See note below for possible workarounds. || ||compare modification times ||`[[ file1 -nt file2 ]]` or `-ot` ||`[ "$(find 'file1' -prune -newer 'file2')" ]` or `[ "file1" -nt "file2" ]` ||`-prune` is required to avoid recursion; present in ksh. `-nt` and `-ot` aren't specified by POSIX. || ||check if 2 files are the same hardlink ||`[[ file1 -ef file2 ]]` ||`[ "file1" -ef "file2" ]` ||`-et` is not defined by POSIX, but is present in ksh and Dash. || ||`(( ))` ||`(( ))` (without the `$`) acts like a command on its own ||For simple comparison: `[ -lt ] (and -ne -gt -ge)` or `[ "$(( 3+1 < 5))" -eq 0 ]`. ||present in ksh || || || ||To assign a variable `var=$((3+1))` || || Note: several standard POSIX utilities can be used for lexical comparisons. The examples below return a true (zero) exit status if the content of `$a` sorts before `$b`. * `awk -v v1="$a" -v v2="$b" 'BEGIN { exit !(v1 "" < "" v2) }'` * `expr "x$a" "<" "x$b" >/dev/null` * `printf "%s\n" "x$a" "x$b" | sort -c >/dev/null 2>&1` (also returns true if `$a` and `$b` are equal) == Arithmetic == || ||'''Works in bash''' ||'''Change to for dash''' ||'''Comment''' || ||pre/pos increment/decrement ||`++ --` ||`i=$((i+1))` ||- || ||- ||`let` ||`: $((i=i+1))` ||The `:` command can be used to peform side effects with an expansion || == Redirections == || ||'''Works in bash''' ||'''Change to for dash''' ||'''Comment''' || ||redirect both stdout and stderr ||`>&` and `&>` ||`command > file 2>&1 or commnd 2>&1 | othercommand` ||- || || ||`|&` (bash4) ||`command 2>&1 | othercommand` ||- || ||duplicate and close ||`m>&n- m<&n-` ||`m>&n n>&-` ||not defined by POSIX || ||herestring ||`<<<"string"` ||`echo | command`, or a here document to avoid a subshell (`<<EOF`) ||- || == Builtins == * `echo -n` or `-e` -- POSIX doesn't define any options, and furthermore allows `echo -e` to be the default behavior. Instead use `printf "%s\n"` (for normal echo) or `printf "%b\n"` (for `echo -e`); leave off the `\n` to simulate `echo -n`. * `printf -v` is not defined by POSIX. Also the `%q` format is not defined by POSIX. * `read` -- the only option defined by POSIX is `-r`; ksh has a different set of options that only partially overlaps with bash. * `shopt`, and therefore all the options it provides (`extglob`, `nullglob`, `dotglob`, etc.) are not defined by POSIX and are bash-specific * `local` -- there is no POSIX equivalent. You can use `$funcname_varname` to reduce the likelihood of conflicts, but even that is not enough for recursive functions. You can ensure that recursive calls occur in subshell environments (so there is a "local" copy of all variables), or pass all "local variables" as parameters (because the positional parameters `$@`, `$1`, `$2`, etc are truly local). dash explicitly supports `local` as a non-Posix extension; ksh uses `typeset` instead, which works like bash's `declare`. == Special Variables == || ||'''Works in bash''' ||'''Change to for dash''' ||'''Comment''' || ||keep track of the times ||`SECONDS` ||`before=$(date +%s) ....seconds=$(( $(date +%s) - $before))` ||`date +%s` is not POSIX; see [[BashFAQ/070|this faq for more info]]. Present in ksh || ||Generate a random number ||`RANDOM` ||{{{random=$(awk 'BEGIN{srand(); printf "%d\n",(rand()*256)}')}}} gives a number between 0 and 256<<BR>> {{{random=$(hexdump -n 1 -e '/1 "%u"' /dev/urandom)}}} and {{{random=$(od -A n -N 1 -t u1 /dev/urandom)}}} give a timer-independent number between 0 and 256<<BR>> {{{random=$(hexdump -n 2 -e '/2 "%u"' /dev/urandom)}}} and {{{random=$(od -A n -N 2 -t u2 /dev/urandom)}}} give a timer-independent number between 0 and 65535 ||Be sure to learn what `srand()` and `rand()` do, ie this method fails if you call `awk` several times rapidly. Instead generate all the numbers you need inside `awk`. Some systems also provide /dev/random and /dev/urandom , but this is not necessarily mandated by the POSIX standard. ksh has `RANDOM` || ||Get the status of all the commands in a pipeline ||`PIPESTATUS` ||Simplest solution:<<BR>> {{{mkfifo fifo; command2 <fifo & command1 >fifo; echo $?}}}<<BR>> see NamedPipes ||bash-specific; see [[http://shell.cfajohnson.com/cus-faq-2.html#Q11|this faq]] and this script [[http://pipestatus.sourceforge.net/|pipe status for POSIX shell]] || == More == * [[http://www.gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html#Bash-POSIX-Mode|The bash manual]] has a list of the differences between bash running in POSIX mode and a normal bash. Note that bash in POSIX mode is only guaranteed to run a shell written according to the POSIX specification. It ''doesn't'' mean that it will fail if you use bashisms in your scripts. * There is a handy perl script checkbashisms which is part of the debian devscripts package which can help point out bashisms in a particular script. ---- CategoryShell |
Name: Kira Bohr<<BR>><<BR>> Age: 28 years old<<BR>><<BR>> Country: United States<<BR>><<BR>> Home town: Warrensville Heights <<BR>><<BR>> Postal code: 44128<<BR>><<BR>> Street: 1590 Parker Drive<<BR>><<BR>> <<BR>><<BR>> my web page ... The Watch Hut - view all voucher codes; [[http://www.promotionvoucher.co.uk/vouchers/thewatchhut/|http://www.promotionvoucher.co.uk/]], |
Name: Kira Bohr
Age: 28 years old
Country: United States
Home town: Warrensville Heights
Postal code: 44128
Street: 1590 Parker Drive
my web page ... The Watch Hut - view all voucher codes; http://www.promotionvoucher.co.uk/,