Differences between revisions 34 and 76 (spanning 42 versions)
Revision 34 as of 2011-08-01 17:24:46
Size: 8795
Editor: albemarle
Comment: Added /dev/urandom-based (and thus timer-independent) alternatives to $RANDOM
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"` || `$' '` 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. A possible workaround with awk: `awk -v v1="1" -v v2="fcd" 'BEGIN{exit !(v1 "" < "" v2)}'` ||
|| compare modification times || `[[ file1 -nt file2 ]]` or `-ot` || `[ $(find 'file1' -prune -newer 'file2') ]` || `-prune` is required to avoid recursion; present in ksh ||
|| check if 2 files are the same hardlink || `[[ file1 -ef file2 ]]` || ? || `-et` is not defined by POSIX, but is present in ksh ||
|| `(( ))` || `(( ))` (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))` || ||

== 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, {{{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, and {{{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 {{{mkfifo fifo; command2 <fifo & command1 >fifo; echo $?}}} 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)