Differences between revisions 41 and 76 (spanning 35 versions)
Revision 41 as of 2012-07-25 18:30:57
Size: 9046
Editor: 38
Comment:
Revision 76 as of 2016-04-25 22:59:36
Size: 380
Editor: KiraBohr3
Comment:
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" ]` ||`-ef` 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/,

Bashism (last edited 2022-10-20 23:13:29 by larryv)