Correct html quote char

This commit is contained in:
Rawiri Blundell 2023-04-24 23:27:29 +12:00
parent 0e677b8e52
commit 39591d2ff9
57 changed files with 605 additions and 600 deletions

View File

@ -107,7 +107,12 @@ scrape_targets() {
# This should remove everything above and below our desired Dokuwiki Markup # This should remove everything above and below our desired Dokuwiki Markup
# We also take the opportunity to convert some HTML chars # We also take the opportunity to convert some HTML chars
extract_markup() { extract_markup() {
sed -e '1,/name="sectok"/d' -e '/<\/textarea>/,$d' -e 's/&gt;/>/g' -e 's/&lt;/</g' -e 's/&amp;/\&/g' "${1:-/dev/stdin}" sed -e '1,/name="sectok"/d' \
-e '/<\/textarea>/,$d' \
-e 's/&gt;/>/g' \
-e 's/&lt;/</g' \
-e 's/&amp;/\&/g' \
-e 's/&quot;/"/g' "${1:-/dev/stdin}"
} }
###### Beyond this point things get a little wishy-washy ###### ###### Beyond this point things get a little wishy-washy ######

View File

@ -152,9 +152,9 @@ something like
declare -A ASSOC declare -A ASSOC
ASSOC[First]=&quot;first element&quot; ASSOC[First]="first element"
ASSOC[Hello]=&quot;second element&quot; ASSOC[Hello]="second element"
ASSOC[Peter Pan]=&quot;A weird guy&quot; ASSOC[Peter Pan]="A weird guy"
See [arrays](/syntax/arrays) See [arrays](/syntax/arrays)

View File

@ -38,11 +38,11 @@ die() {
while caller $frame; do while caller $frame; do
((++frame)); ((++frame));
done done
echo &quot;$*&quot; echo "$*"
exit 1 exit 1
} }
f1() { die &quot;*** an error occured ***&quot;; } f1() { die "*** an error occured ***"; }
f2() { f1; } f2() { f1; }
f3() { f2; } f3() { f2; }

View File

@ -79,11 +79,11 @@ assignment.
$ declare -f $ declare -f
foo () foo ()
{ {
echo &quot;FOO is BAR&quot; echo "FOO is BAR"
} }
world () world ()
{ {
echo &quot;Hello World!&quot; echo "Hello World!"
} }
...or just a specific defined function. ...or just a specific defined function.
@ -91,7 +91,7 @@ assignment.
$ declare -f foo $ declare -f foo
foo () foo ()
{ {
echo &quot;FOO is BAR&quot; echo "FOO is BAR"
} }
### Nameref ### Nameref
@ -107,15 +107,15 @@ variable whose name is expanded on the RHS.
typeset -n _result=$1 _arr typeset -n _result=$1 _arr
typeset IFS=+ typeset IFS=+
_result=0 _result=0
for _arr in &quot;${@:2}&quot;; do # Demonstrate the special property of &quot;for&quot; on a nameref. for _arr in "${@:2}"; do # Demonstrate the special property of "for" on a nameref.
(( _result += ${_arr[*]} )) (( _result += ${_arr[*]} ))
printf '%s = %d\n' &quot;${!_result}&quot; &quot;$_result&quot; # Demonstrate the special property of ${!ref} on a nameref. printf '%s = %d\n' "${!_result}" "$_result" # Demonstrate the special property of ${!ref} on a nameref.
done done
} }
a=(1 2 3) b=(6 5 4) c=(2 4 6) a=(1 2 3) b=(6 5 4) c=(2 4 6)
sum total a b c sum total a b c
printf 'Final value of &quot;total&quot; is: %d\n' &quot;$total&quot; printf 'Final value of "total" is: %d\n' "$total"
\<div hide\> function sum { \<div hide\> function sum {
@ -123,10 +123,10 @@ variable whose name is expanded on the RHS.
shift shift
typeset IFS=+ _arrx typeset IFS=+ _arrx
_result=0 _result=0
for _arrx in &quot;$@&quot;; do # Demonstrate the special property of &quot;for&quot; on a nameref. for _arrx in "$@"; do # Demonstrate the special property of "for" on a nameref.
typeset -n _arr=$_arrx typeset -n _arr=$_arrx
(( _result += ${_arr[*]} )) (( _result += ${_arr[*]} ))
printf '%s = %d\n' &quot;${!_result}&quot; &quot;$_result&quot; # Demonstrate the special property of ${!ref} on a nameref. printf '%s = %d\n' "${!_result}" "$_result" # Demonstrate the special property of ${!ref} on a nameref.
done done
} }

View File

@ -27,7 +27,7 @@ the `eval` command below it.
... arbitrary bash code here ... ... arbitrary bash code here ...
EOF EOF
eval &quot;$myCode&quot; eval "$myCode"
### Expansion side-effects ### Expansion side-effects
@ -41,16 +41,16 @@ This code defines a set of identical functions using the supplied names.
`eval` is the only way to achieve this effect. `eval` is the only way to achieve this effect.
main() { main() {
local fun='() { echo &quot;$FUNCNAME&quot;; }' x local fun='() { echo "$FUNCNAME"; }' x
for x in {f..n}; do for x in {f..n}; do
eval &quot;${x}${fun}&quot; eval "${x}${fun}"
done done
&quot;$@&quot; "$@"
} }
main &quot;$@&quot; main "$@"
### Using printf %q ### Using printf %q
@ -58,8 +58,8 @@ The `printf %q` format string performs shell escaping on its arguments.
This makes `printf %q` the "anti-eval" - with each pass of a string This makes `printf %q` the "anti-eval" - with each pass of a string
through printf requiring another `eval` to peel off the escaping again. through printf requiring another `eval` to peel off the escaping again.
while (( ++n <= 5 )) || ! evalBall=&quot;eval $evalBall&quot;; do while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do
printf -v evalBall 'eval %q' &quot;printf $n;${evalBall-printf '0\n'}&quot; printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}"
done done
$evalBall $evalBall
@ -78,19 +78,19 @@ application](http://en.wikipedia.org/wiki/Partial_application) using
`eval`. `eval`.
function partial { function partial {
eval shift 2 \; function &quot;$1&quot; \{ &quot;$2&quot; &quot;$(printf '%q ' &quot;${@:3}&quot;)&quot; '&quot;$@&quot;; }' eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }'
} }
function repeat { function repeat {
[[ $1 == +([0-9]) ]] || return [[ $1 == +([0-9]) ]] || return
typeset n typeset n
while ((n++ < $1)); do while ((n++ < $1)); do
&quot;${@:2}&quot; "${@:2}"
done done
} }
partial print3 repeat 3 printf '%s ' # Create a new function named print3 partial print3 repeat 3 printf '%s ' # Create a new function named print3
print3 hi # Print &quot;hi&quot; 3 times print3 hi # Print "hi" 3 times
echo echo
This is very easy to do incorrectly and not usually considered idiomatic This is very easy to do incorrectly and not usually considered idiomatic
@ -120,11 +120,11 @@ controlled carefully by the caller is a good way to use it.
<!-- --> <!-- -->
$ ( eval a=( a b\\ c d ); printf '<%s> ' &quot;${a[@]}&quot;; echo ) # Only works in Bash. $ ( eval a=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Only works in Bash.
<a> <b c> <d> <a> <b c> <d>
$ ( x=a; eval &quot;$x&quot;=( a b\\ c d ); printf '<%s> ' &quot;${a[@]}&quot;; echo ) # Argument is no longer in the form of a valid assignment, therefore ordinary parsing rules apply. $ ( x=a; eval "$x"=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Argument is no longer in the form of a valid assignment, therefore ordinary parsing rules apply.
-bash: syntax error near unexpected token `(' -bash: syntax error near unexpected token `('
$ ( x=a; eval &quot;$x&quot;'=( a b\ c d )'; printf '<%s> ' &quot;${a[@]}&quot;; echo ) # Proper quoting then gives us the expected results. $ ( x=a; eval "$x"'=( a b\ c d )'; printf '<%s> ' "${a[@]}"; echo ) # Proper quoting then gives us the expected results.
<a> <b c> <d> <a> <b c> <d>
We don't know why Bash does this. Since parentheses are metacharacters, We don't know why Bash does this. Since parentheses are metacharacters,
@ -138,7 +138,7 @@ is still subject to all expansions including
[word-splitting](syntax/expansion/wordsplit) and [pathname [word-splitting](syntax/expansion/wordsplit) and [pathname
expansion](syntax/expansion/glob). expansion](syntax/expansion/glob).
$ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo &quot;${x[@]}&quot; ) $ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" )
+ touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)' + touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
+ eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)' + eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'
++ x+=(\[[123]\]=*) ++ x+=(\[[123]\]=*)

View File

@ -38,12 +38,12 @@ shell without executing any program.
``` bash ``` bash
myprog=/bin/ls myprog=/bin/ls
echo &quot;This is the wrapper script, it will exec $myprog&quot; echo "This is the wrapper script, it will exec $myprog"
# do some vodoo here, probably change the arguments etc. # do some vodoo here, probably change the arguments etc.
# well, stuff a wrapper is there for # well, stuff a wrapper is there for
exec &quot;$myprog&quot; &quot;$@&quot; exec "$myprog" "$@"
``` ```
### Open a file as input for the script ### Open a file as input for the script

View File

@ -35,7 +35,7 @@ An argument of `--` disables further option processing.
Set the display to use when launching a GUI application (useful during Set the display to use when launching a GUI application (useful during
SSH sessions): SSH sessions):
export DISPLAY=&quot;:0&quot; export DISPLAY=":0"
Set your default text editor (e.g. SublimeText): Set your default text editor (e.g. SublimeText):

View File

@ -30,15 +30,15 @@ for arithmetic). For this reason, **the [arithmetic compound
command](/syntax/ccmd/arithmetic_eval) should generally be preferred command](/syntax/ccmd/arithmetic_eval) should generally be preferred
over `let`**. over `let`**.
$ let 'b = a' &quot;(a += 3) + $((a = 1)), b++&quot; $ let 'b = a' "(a += 3) + $((a = 1)), b++"
$ echo &quot;$a - $b - $?&quot; $ echo "$a - $b - $?"
4 - 2 - 0 4 - 2 - 0
Is equivalent to the [arithmetic evaluation compound Is equivalent to the [arithmetic evaluation compound
command](/syntax/ccmd/arithmetic_eval): command](/syntax/ccmd/arithmetic_eval):
$ (( b = a, (a += 3) + $((a = 1)), b++ )) $ (( b = a, (a += 3) + $((a = 1)), b++ ))
$ echo &quot;$a - $b - $?&quot; $ echo "$a - $b - $?"
4 - 2 - 0 4 - 2 - 0
\<WRAP info\> Remember that inside arithmetic evaluation contexts, all \<WRAP info\> Remember that inside arithmetic evaluation contexts, all
@ -57,12 +57,12 @@ assignments to regular builtins are always local even if the variable is
modified by the builtin. modified by the builtin.
~ $ ( y=1+1 let x=y; declare -p x y ) ~ $ ( y=1+1 let x=y; declare -p x y )
declare -- x=&quot;2&quot; declare -- x="2"
bash: declare: y: not found bash: declare: y: not found
~ $ ( y=1+1 let x=y++; declare -p x y ) ~ $ ( y=1+1 let x=y++; declare -p x y )
declare -- x=&quot;2&quot; declare -- x="2"
declare -- y=&quot;3&quot; declare -- y="3"
This can be useful in certain situations where a temporary variable is This can be useful in certain situations where a temporary variable is
needed. needed.
@ -70,7 +70,7 @@ needed.
## Portability considerations ## Portability considerations
- the `let` command is not specified by POSIX(r). The portable - the `let` command is not specified by POSIX(r). The portable
alternative is: `[ &quot;$(( <EXPRESSION> ))&quot; -ne 0 ]`. To make alternative is: `[ "$(( <EXPRESSION> ))" -ne 0 ]`. To make
portable scripts simpler and cleaner, `let` can be defined as: portable scripts simpler and cleaner, `let` can be defined as:
`# POSIX `# POSIX
let() { let() {

View File

@ -56,7 +56,7 @@ arguments are guaranteed to be passed to a single invocation of the
command with no wordsplitting, pathname expansion, or other monkey command with no wordsplitting, pathname expansion, or other monkey
business. business.
# eix --only-names -IC x11-drivers | { mapfile -t args; emerge -av1 &quot;${args[@]}&quot; <&1; } # eix --only-names -IC x11-drivers | { mapfile -t args; emerge -av1 "${args[@]}" <&1; }
Note the use of command grouping to keep the emerge command inside the Note the use of command grouping to keep the emerge command inside the
pipe's subshell and within the scope of "args". Also note the unusual pipe's subshell and within the scope of "args". Also note the unusual
@ -94,7 +94,7 @@ of a function, with the extra words passed to it as arguments. If you're
going to use callbacks at all, this is probably the best way because it going to use callbacks at all, this is probably the best way because it
allows for easy access to the arguments with no ugly "code in a string". allows for easy access to the arguments with no ugly "code in a string".
$ foo() { echo &quot;|$1|&quot;; }; mapfile -n 11 -c 2 -C 'foo' <file $ foo() { echo "|$1|"; }; mapfile -n 11 -c 2 -C 'foo' <file
|2| |2|
|4| |4|
etc.. etc..
@ -105,7 +105,7 @@ every line of some input, and then output even and odd lines to separate
files. This is far from the best possible answer, but hopefully files. This is far from the best possible answer, but hopefully
illustrates the callback behavior: illustrates the callback behavior:
$ { printf 'input%s\n' {1..10} | mapfile -c 1 -C '>&$(( (${#x[@]} % 2) + 3 )) printf -- &quot;%.sprefix %s&quot;' x; } 3>outfile0 4>outfile1 $ { printf 'input%s\n' {1..10} | mapfile -c 1 -C '>&$(( (${#x[@]} % 2) + 3 )) printf -- "%.sprefix %s"' x; } 3>outfile0 4>outfile1
$ cat outfile{0,1} $ cat outfile{0,1}
prefix input1 prefix input1
prefix input3 prefix input3
@ -131,7 +131,7 @@ reader. This is quite the hack but illustrates some interesting
properties of printf -v and mapfile -C (which you should probably never properties of printf -v and mapfile -C (which you should probably never
use in real code). use in real code).
$ y=( 'odd[j]' 'even[j++]' ); printf 'input%s\n' {1..10} | { mapfile -tc 1 -C 'printf -v &quot;${y[${#x[@]} % 2]}&quot; -- &quot;%.sprefix %s&quot;' x; printf '%s\n' &quot;${odd[@]}&quot; '' &quot;${even[@]}&quot;; } $ y=( 'odd[j]' 'even[j++]' ); printf 'input%s\n' {1..10} | { mapfile -tc 1 -C 'printf -v "${y[${#x[@]} % 2]}" -- "%.sprefix %s"' x; printf '%s\n' "${odd[@]}" '' "${even[@]}"; }
prefix input1 prefix input1
prefix input3 prefix input3
prefix input5 prefix input5
@ -154,7 +154,7 @@ and returns the record.
#!/usr/bin/env bash #!/usr/bin/env bash
showRecord() { showRecord() {
printf 'key[%d] = %d, %d\n' &quot;$1&quot; &quot;${vals[@]:keys[$1]*2:2}&quot; printf 'key[%d] = %d, %d\n' "$1" "${vals[@]:keys[$1]*2:2}"
} }
parseRecords() { parseRecords() {
@ -167,17 +167,17 @@ and returns the record.
local n local n
_f _f
mapfile -tc2 -C _f &quot;$1&quot; mapfile -tc2 -C _f "$1"
eval &quot;$1&quot;'=(&quot;${'&quot;$1&quot;'[@]##*:}&quot;)' # Return the array with some modification eval "$1"'=("${'"$1"'[@]##*:}")' # Return the array with some modification
} }
main() { main() {
local -a keys vals local -a keys vals
parseRecords vals parseRecords vals
showRecord &quot;$1&quot; showRecord "$1"
} }
main &quot;$1&quot; <<-&quot;EOF&quot; main "$1" <<-"EOF"
fabric.domain:123 fabric.domain:123
routex:1 routex:1
routey:2 routey:2

View File

@ -41,7 +41,7 @@ formatstring may point to are given after that, here, indicated by
Thus, a typical `printf`-call looks like: Thus, a typical `printf`-call looks like:
printf &quot;Surname: %s\nName: %s\n&quot; &quot;$SURNAME&quot; &quot;$FIRSTNAME&quot; printf "Surname: %s\nName: %s\n" "$SURNAME" "$FIRSTNAME"
where `"Surname: %s\nName: %s\n"` is the format specification, and the where `"Surname: %s\nName: %s\n"` is the format specification, and the
two variables are passed as arguments, the `%s` in the formatstring two variables are passed as arguments, the `%s` in the formatstring
@ -61,9 +61,9 @@ older than Bash 4.1.
performing expansions into the first non-option argument of printf as performing expansions into the first non-option argument of printf as
this opens up the possibility of an easy code injection vulnerability. this opens up the possibility of an easy code injection vulnerability.
$ var='-vx[$(echo hi >&2)]'; printf &quot;$var&quot; hi; declare -p x $ var='-vx[$(echo hi >&2)]'; printf "$var" hi; declare -p x
hi hi
declare -a x='([0]=&quot;hi&quot;)' declare -a x='([0]="hi")'
...where the echo can of course be replaced with any arbitrary command. ...where the echo can of course be replaced with any arbitrary command.
If you must, either specify a hard-coded format string or use -- to If you must, either specify a hard-coded format string or use -- to
@ -159,7 +159,7 @@ To be more flexible in the output of numbers and strings, the `printf`
command allows format modifiers. These are specified **between** the command allows format modifiers. These are specified **between** the
introductory `%` and the character that specifies the format: introductory `%` and the character that specifies the format:
printf &quot;%50s\n&quot; &quot;This field is 50 characters wide...&quot; printf "%50s\n" "This field is 50 characters wide..."
#### Field and printing modifiers #### Field and printing modifiers
@ -191,7 +191,7 @@ The precision for a floating- or double-number can be specified by using
`<DIGITS>` is an asterisk (`*`), the precision is read from the argument `<DIGITS>` is an asterisk (`*`), the precision is read from the argument
that precedes the number to print, like (prints 4,3000000000): that precedes the number to print, like (prints 4,3000000000):
printf &quot;%.*f\n&quot; 10 4,3 printf "%.*f\n" 10 4,3
The format `.*N` to specify the N'th argument for precision does not The format `.*N` to specify the N'th argument for precision does not
work in Bash. work in Bash.
@ -271,7 +271,7 @@ This small loop prints all numbers from 0 to 127 in
<!-- --> <!-- -->
for ((x=0; x <= 127; x++)); do for ((x=0; x <= 127; x++)); do
printf '%3d | %04o | 0x%02x\n' &quot;$x&quot; &quot;$x&quot; &quot;$x&quot; printf '%3d | %04o | 0x%02x\n' "$x" "$x" "$x"
done done
### Ensure well-formatted MAC address ### Ensure well-formatted MAC address
@ -280,13 +280,13 @@ This code here will take a common MAC address and rewrite it into a
well-known format (regarding leading zeros or upper/lowercase of the hex well-known format (regarding leading zeros or upper/lowercase of the hex
digits, ...): digits, ...):
the_mac=&quot;0:13:ce:7:7a:ad&quot; the_mac="0:13:ce:7:7a:ad"
# lowercase hex digits # lowercase hex digits
the_mac=&quot;$(printf &quot;%02x:%02x:%02x:%02x:%02x:%02x&quot; 0x${the_mac//:/ 0x})&quot; the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/ 0x})"
# or the uppercase-digits variant # or the uppercase-digits variant
the_mac=&quot;$(printf &quot;%02X:%02X:%02X:%02X:%02X:%02X&quot; 0x${the_mac//:/ 0x})&quot; the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})"
### Replacement echo ### Replacement echo
@ -294,16 +294,16 @@ This code was found in Solaris manpage for echo(1).
Solaris version of `/usr/bin/echo` is equivalent to: Solaris version of `/usr/bin/echo` is equivalent to:
printf &quot;%b\n&quot; &quot;$*&quot; printf "%b\n" "$*"
Solaris `/usr/ucb/echo` is equivalent to: Solaris `/usr/ucb/echo` is equivalent to:
if [ &quot;X$1&quot; = &quot;X-n&quot; ] if [ "X$1" = "X-n" ]
then then
shift shift
printf &quot;%s&quot; &quot;$*&quot; printf "%s" "$*"
else else
printf &quot;%s\n&quot; &quot;$*&quot; printf "%s\n" "$*"
fi fi
### prargs Implementation ### prargs Implementation
@ -311,14 +311,14 @@ Solaris `/usr/ucb/echo` is equivalent to:
Working off the replacement echo, here is a terse implementation of Working off the replacement echo, here is a terse implementation of
prargs: prargs:
printf '&quot;%b&quot;\n' &quot;$0&quot; &quot;$@&quot; | nl -v0 -s&quot;: &quot; printf '"%b"\n' "$0" "$@" | nl -v0 -s": "
### repeating a character (for example to print a line) ### repeating a character (for example to print a line)
A small trick: Combining printf and parameter expansion to draw a line A small trick: Combining printf and parameter expansion to draw a line
length=40 length=40
printf -v line '%*s' &quot;$length&quot; printf -v line '%*s' "$length"
echo ${line// /-} echo ${line// /-}
or: or:
@ -350,7 +350,7 @@ In the following example, the two strings are concatenated by the
intervening space so that no argument remains to fill the format. intervening space so that no argument remains to fill the format.
$ echo &quot;Foo&quot; | awk '{ printf &quot;%s\n&quot; $1 }' $ echo "Foo" | awk '{ printf "%s\n" $1 }'
awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
`%s `%s
Foo' Foo'
@ -359,7 +359,7 @@ intervening space so that no argument remains to fill the format.
Simply replacing the space with a comma and adding parentheses yields Simply replacing the space with a comma and adding parentheses yields
correct awk syntax. correct awk syntax.
$ echo &quot;Foo&quot; | awk '{ printf( &quot;%s\n&quot;, $1 ) }' $ echo "Foo" | awk '{ printf( "%s\n", $1 ) }'
Foo Foo
With appropriate metacharacter escaping the bash printf can be called With appropriate metacharacter escaping the bash printf can be called
@ -367,7 +367,7 @@ from inside awk (as from perl and other languages that support shell
callout) as long as you don't care about program efficiency or callout) as long as you don't care about program efficiency or
readability. readability.
echo &quot;Foo&quot; | awk '{ system( &quot;printf \&quot;%s\\n \&quot; \&quot;&quot; $1 &quot;\&quot;&quot; ) }' echo "Foo" | awk '{ system( "printf \"%s\\n \" \"" $1 "\"" ) }'
Foo Foo
## Differences from C, and portability considerations ## Differences from C, and portability considerations
@ -395,7 +395,7 @@ readability.
modifiers specified by POSIX during format string parsing. modifiers specified by POSIX during format string parsing.
``` c|builtins/printf.def ``` c|builtins/printf.def
#define LENMODS &quot;hjlLtz&quot; #define LENMODS "hjlLtz"
... ...
/* skip possible format modifiers */ /* skip possible format modifiers */
modstart = fmt; modstart = fmt;
@ -427,15 +427,15 @@ fmt++;
shift shift
nameref x=$1 nameref x=$1
shift shift
x=$(command printf &quot;$@&quot;) x=$(command printf "$@")
;; ;;
*) *)
command printf &quot;$@&quot; command printf "$@"
esac esac
} }
builtin cut builtin cut
print $$ print $$
printf -v 'foo[2]' '%d\n' &quot;$(cut -d ' ' -f 1 /proc/self/stat)&quot; printf -v 'foo[2]' '%d\n' "$(cut -d ' ' -f 1 /proc/self/stat)"
typeset -p foo typeset -p foo
# 22461 # 22461
# typeset -a foo=([2]=22461) # typeset -a foo=([2]=22461)

View File

@ -27,8 +27,8 @@ the line as it was read, without stripping pre- and postfix spaces and
other things! other things!
while read -r; do while read -r; do
printf '&quot;%s&quot;\n' &quot;$REPLY&quot; printf '"%s"\n' "$REPLY"
done <<<&quot; a line with prefix and postfix space &quot; done <<<" a line with prefix and postfix space "
\</WRAP\> \</WRAP\>
@ -108,13 +108,13 @@ spec.
2012-05-23 13:49:00 ormaaj so that's what read without -r does? 2012-05-23 13:49:00 ormaaj so that's what read without -r does?
2012-05-23 13:49:16 geirha no, -r doesn't remove the backslashes 2012-05-23 13:49:16 geirha no, -r doesn't remove the backslashes
2012-05-23 13:49:34 ormaaj I thought read <<<'str' was equivalent to read -r <<<$'str' 2012-05-23 13:49:34 ormaaj I thought read <<<'str' was equivalent to read -r <<<$'str'
2012-05-23 13:49:38 geirha # read x y <<< 'foo\ bar baz'; echo &quot;<$x><$y>&quot; 2012-05-23 13:49:38 geirha # read x y <<< 'foo\ bar baz'; echo "<$x><$y>"
2012-05-23 13:49:40 shbot geirha: <foo bar><baz> 2012-05-23 13:49:40 shbot geirha: <foo bar><baz>
2012-05-23 13:50:32 geirha no, read without -r is mostly pointless. Damn bourne 2012-05-23 13:50:32 geirha no, read without -r is mostly pointless. Damn bourne
2012-05-23 13:51:08 ormaaj So it's mostly (entirely) used to escape spaces 2012-05-23 13:51:08 ormaaj So it's mostly (entirely) used to escape spaces
2012-05-23 13:51:24 ormaaj and insert newlines 2012-05-23 13:51:24 ormaaj and insert newlines
2012-05-23 13:51:47 geirha ormaaj: you mostly get the same effect as using \ at the prompt 2012-05-23 13:51:47 geirha ormaaj: you mostly get the same effect as using \ at the prompt
2012-05-23 13:52:04 geirha echo \&quot; outputs a &quot; , read x <<< '\&quot;' reads a &quot; 2012-05-23 13:52:04 geirha echo \" outputs a " , read x <<< '\"' reads a "
2012-05-23 13:52:32 ormaaj oh weird 2012-05-23 13:52:32 ormaaj oh weird
2012-05-23 13:52:46 * ormaaj struggles to think of a point to that... 2012-05-23 13:52:46 * ormaaj struggles to think of a point to that...
2012-05-23 13:53:01 geirha ormaaj: ask Bourne :P 2012-05-23 13:53:01 geirha ormaaj: ask Bourne :P
@ -130,8 +130,8 @@ from a file and print them on the terminal.
opossum() { opossum() {
while read -r; do while read -r; do
printf &quot;%s\n&quot; &quot;$REPLY&quot; printf "%s\n" "$REPLY"
done <&quot;$1&quot; done <"$1"
} }
<u>**Note:**</u> Here, `read -r` and the default `REPLY` is used, <u>**Note:**</u> Here, `read -r` and the default `REPLY` is used,
@ -145,7 +145,7 @@ Remember the MSDOS `pause` command? Here's something similar:
pause() { pause() {
local dummy local dummy
read -s -r -p &quot;Press any key to continue...&quot; -n 1 dummy read -s -r -p "Press any key to continue..." -n 1 dummy
} }
Notes: Notes:
@ -160,14 +160,14 @@ Notes:
Read can be used to split a string: Read can be used to split a string:
var=&quot;one two three&quot; var="one two three"
read -r col1 col2 col3 <<< &quot;$var&quot; read -r col1 col2 col3 <<< "$var"
printf &quot;col1: %s col2: %s col3 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot; printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
Take care that you cannot use a pipe: Take care that you cannot use a pipe:
echo &quot;$var&quot; | read col1 col2 col3 # does not work! echo "$var" | read col1 col2 col3 # does not work!
printf &quot;col1: %s col2: %s col3 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot; printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
Why? because the commands of the pipe run in subshells that cannot Why? because the commands of the pipe run in subshells that cannot
modify the parent shell. As a result, the variables `col1`, `col2` and modify the parent shell. As a result, the variables `col1`, `col2` and
@ -177,8 +177,8 @@ modify the parent shell. As a result, the variables `col1`, `col2` and
If the variable has more fields than there are variables, the last If the variable has more fields than there are variables, the last
variable get the remaining of the line: variable get the remaining of the line:
read col1 col2 col3 <<< &quot;one two three four&quot; read col1 col2 col3 <<< "one two three four"
printf &quot;%s\n&quot; &quot;$col3&quot; #prints three four printf "%s\n" "$col3" #prints three four
#### Changing The Separator #### Changing The Separator
@ -186,8 +186,8 @@ By default reads separates the line in fields using spaces or tabs. You
can modify this using the *special variable* can modify this using the *special variable*
[IFS](/syntax/shellvars#IFS), the Internal Field Separator. [IFS](/syntax/shellvars#IFS), the Internal Field Separator.
IFS=&quot;:&quot; read -r col1 col2 <<< &quot;hello:world&quot; IFS=":" read -r col1 col2 <<< "hello:world"
printf &quot;col1: %s col2: %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; printf "col1: %s col2: %s\n" "$col1" "$col2"
Here we use the `var=value command` syntax to set the environment of Here we use the `var=value command` syntax to set the environment of
`read` temporarily. We could have set `IFS` normally, but then we would `read` temporarily. We could have set `IFS` normally, but then we would
@ -198,8 +198,8 @@ The default `IFS` is special in that 2 fields can be separated by one or
more space or tab. When you set `IFS` to something besides whitespace more space or tab. When you set `IFS` to something besides whitespace
(space or tab), the fields are separated by **exactly** one character: (space or tab), the fields are separated by **exactly** one character:
IFS=&quot;:&quot; read -r col1 col2 col3 <<< &quot;hello::world&quot; IFS=":" read -r col1 col2 col3 <<< "hello::world"
printf &quot;col1: %s col2: %s col3 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot; printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3"
See how the `::` in the middle infact defines an additional *empty See how the `::` in the middle infact defines an additional *empty
field*. field*.
@ -207,13 +207,13 @@ field*.
The fields are separated by exactly one character, but the character can The fields are separated by exactly one character, but the character can
be different between each field: be different between each field:
IFS=&quot;:|@&quot; read -r col1 col2 col3 col4 <<< &quot;hello:world|in@bash&quot; IFS=":|@" read -r col1 col2 col3 col4 <<< "hello:world|in@bash"
printf &quot;col1: %s col2: %s col3 %s col4 %s\n&quot; &quot;$col1&quot; &quot;$col2&quot; &quot;$col3&quot; &quot;$col4&quot; printf "col1: %s col2: %s col3 %s col4 %s\n" "$col1" "$col2" "$col3" "$col4"
### Are you sure? ### Are you sure?
asksure() { asksure() {
echo -n &quot;Are you sure (Y/N)? &quot; echo -n "Are you sure (Y/N)? "
while read -r -n 1 -s answer; do while read -r -n 1 -s answer; do
if [[ $answer = [YyNn] ]]; then if [[ $answer = [YyNn] ]]; then
[[ $answer = [Yy] ]] && retval=0 [[ $answer = [Yy] ]] && retval=0
@ -229,16 +229,16 @@ be different between each field:
### using it ### using it
if asksure; then if asksure; then
echo &quot;Okay, performing rm -rf / then, master....&quot; echo "Okay, performing rm -rf / then, master...."
else else
echo &quot;Pfff...&quot; echo "Pfff..."
fi fi
### Ask for a path with a default value ### Ask for a path with a default value
<u>**Note:**</u> The `-i` option was introduced with Bash 4 <u>**Note:**</u> The `-i` option was introduced with Bash 4
read -e -p &quot;Enter the path to the file: &quot; -i &quot;/usr/local/etc/&quot; FILEPATH read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
The user will be prompted, he can just accept the default, or edit it. The user will be prompted, he can just accept the default, or edit it.
@ -247,8 +247,8 @@ The user will be prompted, he can just accept the default, or edit it.
Here, `IFS` contains both, a colon and a space. The fields of the Here, `IFS` contains both, a colon and a space. The fields of the
date/time string are recognized correctly. date/time string are recognized correctly.
datetime=&quot;2008:07:04 00:34:45&quot; datetime="2008:07:04 00:34:45"
IFS=&quot;: &quot; read -r year month day hour minute second <<< &quot;$datetime&quot; IFS=": " read -r year month day hour minute second <<< "$datetime"
## Portability considerations ## Portability considerations

View File

@ -59,7 +59,7 @@ There are no options.
[shift_verbose](internals/shell_options#shift_verbose) [shift_verbose](internals/shell_options#shift_verbose)
[shopt](commands/builtin/shopt) option is enabled. Ksh93, pdksh, posh, [shopt](commands/builtin/shopt) option is enabled. Ksh93, pdksh, posh,
mksh, and dash, all throw useless fatal shell mksh, and dash, all throw useless fatal shell
errors.`$ dash -c 'f() { if shift; then echo &quot;$1&quot;; else echo &quot;no args&quot;; fi; }; f' errors.`$ dash -c 'f() { if shift; then echo "$1"; else echo "no args"; fi; }; f'
dash: 1: shift: can't shift that many dash: 1: shift: can't shift that many
` In most shells, you can work around this problem using the ` In most shells, you can work around this problem using the
[command](/commands/builtin/command) builtin to suppress fatal errors [command](/commands/builtin/command) builtin to suppress fatal errors

View File

@ -63,12 +63,12 @@ Here's a demonstration of this behavior.
# Direct recursion depth. # Direct recursion depth.
# Search up the stack for the first non-FUNCNAME[1] and count how deep we are. # Search up the stack for the first non-FUNCNAME[1] and count how deep we are.
callDepth() { callDepth() {
# Strip &quot;main&quot; off the end of FUNCNAME[@] if current function is named &quot;main&quot; and # Strip "main" off the end of FUNCNAME[@] if current function is named "main" and
# Bash added an extra &quot;main&quot; for non-interactive scripts. # Bash added an extra "main" for non-interactive scripts.
if [[ main == !(!(&quot;${FUNCNAME[1]}&quot;)|!(&quot;${FUNCNAME[-1]}&quot;)) && $- != *i* ]]; then if [[ main == !(!("${FUNCNAME[1]}")|!("${FUNCNAME[-1]}")) && $- != *i* ]]; then
local -a 'fnames=(&quot;${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}&quot;)' local -a 'fnames=("${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}")'
else else
local -a 'fnames=(&quot;${FUNCNAME[@]:1}&quot;)' local -a 'fnames=("${FUNCNAME[@]:1}")'
fi fi
if (( ! ${#fnames[@]} )); then if (( ! ${#fnames[@]} )); then
@ -86,7 +86,7 @@ Here's a demonstration of this behavior.
# This function is the magic stack walker. # This function is the magic stack walker.
unset2() { unset2() {
unset -v -- &quot;$@&quot; unset -v -- "$@"
} }
f() { f() {
@ -97,11 +97,11 @@ Here's a demonstration of this behavior.
f f
else else
trap 'declare -p a' DEBUG trap 'declare -p a' DEBUG
unset2 a # declare -- a=&quot;5&quot; unset2 a # declare -- a="5"
unset a a # declare -- a=&quot;4&quot; unset a a # declare -- a="4"
unset a # declare -- a=&quot;2&quot; unset a # declare -- a="2"
unset a # ./unset-tests: line 44: declare: a: not found unset a # ./unset-tests: line 44: declare: a: not found
: # declare -- a=&quot;global scope yo&quot; : # declare -- a="global scope yo"
fi fi
} }
@ -112,11 +112,11 @@ Here's a demonstration of this behavior.
output: output:
declare -- a=&quot;5&quot; declare -- a="5"
declare -- a=&quot;4&quot; declare -- a="4"
declare -- a=&quot;2&quot; declare -- a="2"
./unset-tests: line 44: declare: a: not found ./unset-tests: line 44: declare: a: not found
declare -- a=&quot;global scope yo&quot; declare -- a="global scope yo"
Some things to observe: Some things to observe:
@ -143,15 +143,15 @@ Like several other Bash builtins that take parameter names, unset
expands its arguments. expands its arguments.
~ $ ( a=({a..d}); unset 'a[2]'; declare -p a ) ~ $ ( a=({a..d}); unset 'a[2]'; declare -p a )
declare -a a='([0]=&quot;a&quot; [1]=&quot;b&quot; [3]=&quot;d&quot;)' declare -a a='([0]="a" [1]="b" [3]="d")'
As usual in such cases, it's important to quote the args to avoid As usual in such cases, it's important to quote the args to avoid
accidental results such as globbing. accidental results such as globbing.
~ $ ( a=({a..d}) b=a c=d d=1; set -x; unset &quot;${b}[&quot;{2..3}-c\]; declare -p a ) ~ $ ( a=({a..d}) b=a c=d d=1; set -x; unset "${b}["{2..3}-c\]; declare -p a )
+ unset 'a[2-1]' 'a[3-1]' + unset 'a[2-1]' 'a[3-1]'
+ declare -p a + declare -p a
declare -a a='([0]=&quot;a&quot; [3]=&quot;d&quot;)' declare -a a='([0]="a" [3]="d")'
Of course hard to follow indirection is still possible whenever Of course hard to follow indirection is still possible whenever
arithmetic is involved, also as shown above, even without extra arithmetic is involved, also as shown above, even without extra

View File

@ -15,9 +15,9 @@ here by using the command in an if-statement:
# test if /etc/passwd exists # test if /etc/passwd exists
if test -e /etc/passwd; then if test -e /etc/passwd; then
echo &quot;Alright man...&quot; >&2 echo "Alright man..." >&2
else else
echo &quot;Yuck! Where is it??&quot; >&2 echo "Yuck! Where is it??" >&2
exit 1 exit 1
fi fi
@ -37,9 +37,9 @@ Let's rewrite the above example to use it:
# test if /etc/passwd exists # test if /etc/passwd exists
if [ -e /etc/passwd ]; then if [ -e /etc/passwd ]; then
echo &quot;Alright man...&quot; >&2 echo "Alright man..." >&2
else else
echo &quot;Yuck! Where is it??&quot; >&2 echo "Yuck! Where is it??" >&2
exit 1 exit 1
fi fi
@ -53,12 +53,12 @@ some of your music files:
#!/bin/bash #!/bin/bash
mymusic=&quot;/data/music/Van Halen/Van Halen - Right Now.mp3&quot; mymusic="/data/music/Van Halen/Van Halen - Right Now.mp3"
if [ -e &quot;$mymusic&quot; ]; then if [ -e "$mymusic" ]; then
echo &quot;Let's rock&quot; >&2 echo "Let's rock" >&2
else else
echo &quot;No music today, sorry...&quot; >&2 echo "No music today, sorry..." >&2
exit 1 exit 1
fi fi
@ -75,7 +75,7 @@ filenames =\> **too many arguments!**
<u>**Another common mistake**</u> is to provide too **few** arguments: <u>**Another common mistake**</u> is to provide too **few** arguments:
[ &quot;$mystring&quot;!=&quot;test&quot; ] [ "$mystring"!="test" ]
This provides exactly **one** test-argument to the command. With one This provides exactly **one** test-argument to the command. With one
parameter, it defaults to the `-n` test: It tests if a provided string parameter, it defaults to the `-n` test: It tests if a provided string
@ -201,8 +201,8 @@ These rules may seem complex, but it's not so bad in practice. Knowing
them might help you to explain some of the "unexplicable" behaviours you them might help you to explain some of the "unexplicable" behaviours you
might encounter: might encounter:
var=&quot;&quot; var=""
if [ -n $var ]; then echo &quot;var is not empty&quot;; fi if [ -n $var ]; then echo "var is not empty"; fi
This code prints "var is not empty", even though `-n something` is This code prints "var is not empty", even though `-n something` is
supposed to be true if `$var` is not empty - **why?** supposed to be true if `$var` is not empty - **why?**
@ -228,8 +228,8 @@ them with the shell `&&` and `||` **list control operators**.
See this: See this:
if [ -n &quot;$var&quot;] && [ -e &quot;$var&quot;]; then if [ -n "$var"] && [ -e "$var"]; then
echo &quot;\$var is not null and a file named $var exists!&quot; echo "\$var is not null and a file named $var exists!"
fi fi
The return status of AND and OR lists is the exit status of the last The return status of AND and OR lists is the exit status of the last
@ -245,13 +245,13 @@ command executed in the list
The logical operators AND and OR for the test-command itself are `-a` The logical operators AND and OR for the test-command itself are `-a`
and `-o`, thus: and `-o`, thus:
if [ -n &quot;$var&quot; -a -e &quot;$var&quot; ] ; then if [ -n "$var" -a -e "$var" ] ; then
echo &quot;\$var is not null and a file named $var exists&quot; echo "\$var is not null and a file named $var exists"
fi fi
They are **not** `&&` or `||`: They are **not** `&&` or `||`:
$ if [ -n &quot;/tmp&quot; && -d &quot;/tmp&quot;]; then echo true; fi # DOES NOT WORK $ if [ -n "/tmp" && -d "/tmp"]; then echo true; fi # DOES NOT WORK
bash: [: missing `]' bash: [: missing `]'
You might find the error message confusing, `[` does not find the You might find the error message confusing, `[` does not find the
@ -280,12 +280,12 @@ Let's say, we want to check the following two things (AND):
Let's see: Let's see:
if [ -z &quot;false&quot; -a -z &quot;$(echo I am executed >&2)&quot; ] ; then ... if [ -z "false" -a -z "$(echo I am executed >&2)" ] ; then ...
=\> The arguments are all expanded **before** `test` runs, thus the =\> The arguments are all expanded **before** `test` runs, thus the
echo-command **is executed**. echo-command **is executed**.
if [ -z &quot;false&quot; ] && [ -z &quot;$(echo I am not executed >&2)&quot; ]; then... if [ -z "false" ] && [ -z "$(echo I am not executed >&2)" ]; then...
=\> Due to the nature of the `&&` list operator, the second test-command =\> Due to the nature of the `&&` list operator, the second test-command
runs only if the first test-command returns true, our echo-command **is runs only if the first test-command returns true, our echo-command **is
@ -305,10 +305,10 @@ the list way (`&&` and `||`):
That means, **you can get different results**, depending on the manner That means, **you can get different results**, depending on the manner
of use: of use:
$ if [ &quot;true&quot; ] || [ -e /does/not/exist ] && [ -e /does/not/exist ]; then echo true; else echo false; fi $ if [ "true" ] || [ -e /does/not/exist ] && [ -e /does/not/exist ]; then echo true; else echo false; fi
false false
$ if [ &quot;true&quot; -o -e /does/not/exist -a -e /does/not/exist ]; then echo true; else echo false;fi $ if [ "true" -o -e /does/not/exist -a -e /does/not/exist ]; then echo true; else echo false;fi
true true
As a result you have to think about it a little or add precedence As a result you have to think about it a little or add precedence
@ -317,18 +317,18 @@ control (parenthesis).
For `&&` and `||` parenthesis means (shell-ly) grouping the commands, For `&&` and `||` parenthesis means (shell-ly) grouping the commands,
and since `( ... )` introduces a subshell we will use `{ ... }` instead: and since `( ... )` introduces a subshell we will use `{ ... }` instead:
$ if [ &quot;true&quot; ] || { [ -e /does/not/exist ] && [ -e /does/not/exist ] ;} ; then echo true; else echo false; fi $ if [ "true" ] || { [ -e /does/not/exist ] && [ -e /does/not/exist ] ;} ; then echo true; else echo false; fi
true true
For the test command, the precedence parenthesis are, as well, `( )`, For the test command, the precedence parenthesis are, as well, `( )`,
but you need to escape or quote them, so that the shell doesn't try to but you need to escape or quote them, so that the shell doesn't try to
interpret them: interpret them:
$ if [ \( &quot;true&quot; -o -e /does/not/exist \) -a -e /does/not/exist ]; then echo true; else echo false; fi $ if [ \( "true" -o -e /does/not/exist \) -a -e /does/not/exist ]; then echo true; else echo false; fi
false false
# equivalent, but less readable IMHO: # equivalent, but less readable IMHO:
$ if [ '(' &quot;true&quot; -o -e /does/not/exist ')' -a -e /does/not/exist ]; then echo true; else echo false; fi $ if [ '(' "true" -o -e /does/not/exist ')' -a -e /does/not/exist ]; then echo true; else echo false; fi
false false
## NOT ## NOT
@ -339,12 +339,12 @@ keyword `!` or passing `!` as an argument to `test`.
Here `!` negates the exit status of the command `test` which is 0 Here `!` negates the exit status of the command `test` which is 0
(true), and the else part is executed: (true), and the else part is executed:
if ! [ -d '/tmp' ]; then echo &quot;/tmp doesn't exists&quot;; else echo &quot;/tmp exists&quot;; fi if ! [ -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
Here the `test` command itself exits with status 1 (false) and the else Here the `test` command itself exits with status 1 (false) and the else
is also executed: is also executed:
if [ ! -d '/tmp' ]; then echo &quot;/tmp doesn't exists&quot;; else echo &quot;/tmp exists&quot;; fi if [ ! -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; fi
Unlike for AND and OR, both methods for NOT have an identical behaviour, Unlike for AND and OR, both methods for NOT have an identical behaviour,
at least for doing one single test. at least for doing one single test.
@ -368,7 +368,7 @@ which you may have already had yourself**:
Hi All, Hi All,
I've got a script that I'm trying to set up, but it keeps telling me I've got a script that I'm trying to set up, but it keeps telling me
that &quot;[-d command not found&quot;. Can someone please explain what is that "[-d command not found". Can someone please explain what is
wrong with this?: wrong with this?:
@ -381,9 +381,9 @@ which you may have already had yourself**:
{ {
if [-d $i] if [-d $i]
then then
echo &quot;$i is a directory! Yay!&quot; echo "$i is a directory! Yay!"
else else
echo &quot;$i is not a directory!&quot; echo "$i is not a directory!"
fi fi
} }
done done
@ -410,7 +410,7 @@ test command:
> (QUOTED TEXT WAS REMOVED) > (QUOTED TEXT WAS REMOVED)
The shell is first and foremost a way to launch other commands. The The shell is first and foremost a way to launch other commands. The
syntax is simply &quot;if&quot; followed by a command-list, (e.g. if /some/foo; syntax is simply "if" followed by a command-list, (e.g. if /some/foo;
or even if cmd1; cmd2; cmd3; then). Plus the '( ... )' syntax is or even if cmd1; cmd2; cmd3; then). Plus the '( ... )' syntax is
already taken by the use of starting a subshell. already taken by the use of starting a subshell.
@ -468,28 +468,28 @@ test command:
the shell before passing them to the (possibly external) command and the shell before passing them to the (possibly external) command and
disappear entirely. This is why test arguments should always be quoted. disappear entirely. This is why test arguments should always be quoted.
if test -d &quot;$file&quot; if test -d "$file"
if [ -d &quot;$file&quot; ] if [ -d "$file" ]
Actually today test is defined that if only one argument is given as Actually today test is defined that if only one argument is given as
in this case &quot;test FOO&quot; then then test returns true if the argument is in this case "test FOO" then then test returns true if the argument is
non-zero in text length. Because &quot;-d&quot; is non-zero length &quot;test -d&quot; is non-zero in text length. Because "-d" is non-zero length "test -d" is
true. The number of arguments affects how test parses the args. This true. The number of arguments affects how test parses the args. This
avoids a case where depending upon the data may look like a test avoids a case where depending upon the data may look like a test
operator. operator.
DATA=&quot;something&quot; DATA="something"
if test &quot;$DATA&quot; # true, $DATA is non-zero length if test "$DATA" # true, $DATA is non-zero length
DATA=&quot;&quot; DATA=""
if test &quot;$DATA&quot; # false, $DATA is zero length if test "$DATA" # false, $DATA is zero length
But the problem case is how should test handle an argument that looks But the problem case is how should test handle an argument that looks
like an operator? This used to generate errors but now because it is like an operator? This used to generate errors but now because it is
only one argument is defined to be the same as test -n $DATA. only one argument is defined to be the same as test -n $DATA.
DATA=&quot;-d&quot; DATA="-d"
if test &quot;$DATA&quot; # true, $DATA is non-zero length if test "$DATA" # true, $DATA is non-zero length
if test -d # true, same as previous case. if test -d # true, same as previous case.
Because test and [ are possibly external commands all of the parts of Because test and [ are possibly external commands all of the parts of
@ -500,7 +500,7 @@ test command:
Incorrect use generating unlikely to be intended results: Incorrect use generating unlikely to be intended results:
if test 5 > 2 # true, &quot;5&quot; is non-zero length, creates file named &quot;2&quot; if test 5 > 2 # true, "5" is non-zero length, creates file named "2"
Intended use: Intended use:
@ -556,7 +556,7 @@ entries of a directory, if an entry is a directory (`[ -d "$fn" ]`),
print its name: print its name:
for fn in *; do for fn in *; do
[ -d &quot;$fn&quot; ] && echo &quot;$fn&quot; [ -d "$fn" ] && echo "$fn"
done done
## See also ## See also

View File

@ -63,7 +63,7 @@ The **output** is converted to **base 10** by default
## Scale And Base ## Scale And Base
`dc` is a calulator with abitrary precision, by default this precision `dc` is a calulator with abitrary precision, by default this precision
is 0. thus `dc <<< &quot;5 4/p&quot;` prints "1". is 0. thus `dc <<< "5 4/p"` prints "1".
We can increase the precision using the `k` command. It pops the value We can increase the precision using the `k` command. It pops the value
at the top of the stack and uses it as the precision argument: at the top of the stack and uses it as the precision argument:
@ -198,7 +198,7 @@ times, or use a macro.
[dd**] # push a string [dd**] # push a string
sa # save it in register a sa # save it in register a
3 # push 3 on the stack 3 # push 3 on the stack
lax # push the string &quot;dd**&quot; on the stack and execute it lax # push the string "dd**" on the stack and execute it
p # print the result p # print the result
4laxp # same operation for 4, in one line 4laxp # same operation for 4, in one line
EOF EOF
@ -221,7 +221,7 @@ we are used to reading:
Some `dc` have `>R <R =R`, GNU `dc` had some more, check your manual. Some `dc` have `>R <R =R`, GNU `dc` had some more, check your manual.
Note that the test "consumes" its operands: the 2 first elements are Note that the test "consumes" its operands: the 2 first elements are
popped off the stack (you can verify that popped off the stack (you can verify that
`dc <<< &quot;[f]sR 2 1 >R 1 2 >R f&quot;` doesn't print anything) `dc <<< "[f]sR 2 1 >R 1 2 >R f"` doesn't print anything)
Have you noticed how we can *include* a macro (string) in a macro? and Have you noticed how we can *include* a macro (string) in a macro? and
as `dc` relies on a stack we can, in fact, use the macro recursively as `dc` relies on a stack we can, in fact, use the macro recursively

View File

@ -22,9 +22,9 @@ common example is a script that gives the user the option of having
chatter() { chatter() {
if [[ $verbose ]]; then if [[ $verbose ]]; then
chatter() { chatter() {
echo &quot;$@&quot; echo "$@"
} }
chatter &quot;$@&quot; chatter "$@"
else else
chatter() { chatter() {
: :
@ -32,9 +32,9 @@ common example is a script that gives the user the option of having
fi fi
} }
echo &quot;Waiting for 10 seconds.&quot; echo "Waiting for 10 seconds."
for i in {1..10}; do for i in {1..10}; do
chatter &quot;$i&quot; chatter "$i"
sleep 1 sleep 1
done done
@ -56,35 +56,35 @@ FIXME Add more examples!
# various versions of -perm /+ blah blah and hacks # various versions of -perm /+ blah blah and hacks
find() { find() {
hash find || { echo 'find not found!'; exit 1; } hash find || { echo 'find not found!'; exit 1; }
# We can be pretty sure &quot;$0&quot; should be executable. # We can be pretty sure "$0" should be executable.
if [[ $(command find &quot;$0&quot; -executable 2> /dev/null) ]]; then if [[ $(command find "$0" -executable 2> /dev/null) ]]; then
unset -f find # We can just use the command find unset -f find # We can just use the command find
elif [[ $(command find &quot;$0&quot; -perm /u+x 2> /dev/null) ]]; then elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then
find() { find() {
typeset arg args typeset arg args
for arg do for arg do
[[ $arg = -executable ]] && args+=(-perm /u+x) || args+=(&quot;$arg&quot;) [[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg")
done done
command find &quot;${args[@]}&quot; command find "${args[@]}"
} }
elif [[ $(command find &quot;$0&quot; -perm +u+x 2> /dev/null) ]]; then elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then
find() { find() {
typeset arg args typeset arg args
for arg do for arg do
[[ $arg = -executable ]] && args+=(-perm +u+x) || args+=(&quot;$arg&quot;) [[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg")
done done
command find &quot;${args[@]}&quot; command find "${args[@]}"
} }
else # Last resort else # Last resort
find() { find() {
typeset arg args typeset arg args
for arg do for arg do
[[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=(&quot;$arg&quot;) [[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg")
done done
command find &quot;${args[@]}&quot; command find "${args[@]}"
} }
fi fi
find &quot;$@&quot; find "$@"
} }
\<code\> \#!/bin/bash \# Using collapsing functions to turn debug \<code\> \#!/bin/bash \# Using collapsing functions to turn debug

View File

@ -10,10 +10,10 @@ Bash source command. The file to be sourced should be formated in
key="value" format, otherwise bash will try to interpret commands: key="value" format, otherwise bash will try to interpret commands:
#!/bin/bash #!/bin/bash
echo &quot;Reading config....&quot; >&2 echo "Reading config...." >&2
source /etc/cool.cfg source /etc/cool.cfg
echo &quot;Config for the username: $cool_username&quot; >&2 echo "Config for the username: $cool_username" >&2
echo &quot;Config for the target host: $cool_host&quot; >&2 echo "Config for the target host: $cool_host" >&2
So, where do these variables come from? If everything works fine, they So, where do these variables come from? If everything works fine, they
are defined in /etc/cool.cfg which is a file that's sourced into the are defined in /etc/cool.cfg which is a file that's sourced into the
@ -21,8 +21,8 @@ current script or shell. Note: this is **not** the same as executing
this file as a script! The sourced file most likely contains something this file as a script! The sourced file most likely contains something
like: like:
cool_username=&quot;guest&quot; cool_username="guest"
cool_host=&quot;foo.example.com&quot; cool_host="foo.example.com"
These are normal statements understood by Bash, nothing special. Of These are normal statements understood by Bash, nothing special. Of
course (and, a big disadvantage under normal circumstances) the sourced course (and, a big disadvantage under normal circumstances) the sourced
@ -33,10 +33,10 @@ The `source` command also is available under the name `.` (dot). The
usage of the dot is identical: usage of the dot is identical:
#!/bin/bash #!/bin/bash
echo &quot;Reading config....&quot; >&2 echo "Reading config...." >&2
. /etc/cool.cfg #note the space between the dot and the leading slash of /etc.cfg . /etc/cool.cfg #note the space between the dot and the leading slash of /etc.cfg
echo &quot;Config for the username: $cool_username&quot; >&2 echo "Config for the username: $cool_username" >&2
echo &quot;Config for the target host: $cool_host&quot; >&2 echo "Config for the target host: $cool_host" >&2
## Per-user configs ## Per-user configs
@ -46,10 +46,10 @@ the following example, the if/then construct is used to check for the
existance of a user-specific config: existance of a user-specific config:
#!/bin/bash #!/bin/bash
echo &quot;Reading system-wide config....&quot; >&2 echo "Reading system-wide config...." >&2
. /etc/cool.cfg . /etc/cool.cfg
if [ -r ~/.coolrc ]; then if [ -r ~/.coolrc ]; then
echo &quot;Reading user config....&quot; >&2 echo "Reading user config...." >&2
. ~/.coolrc . ~/.coolrc
fi fi
@ -68,9 +68,9 @@ malicious code:
username=god_only_knows username=god_only_knows
hostname=www.example.com hostname=www.example.com
password=secret ; echo rm -rf ~/* password=secret ; echo rm -rf ~/*
parameter=foobar && echo &quot;You've bene pwned!&quot;; parameter=foobar && echo "You've bene pwned!";
# hey look, weird code follows... # hey look, weird code follows...
echo &quot;I am the skull virus...&quot; echo "I am the skull virus..."
echo rm -fr ~/* echo rm -fr ~/*
mailto=netadmin@example.com mailto=netadmin@example.com
@ -88,15 +88,15 @@ filters by description:
configfile_secured='/tmp/cool.cfg' configfile_secured='/tmp/cool.cfg'
# check if the file contains something we don't want # check if the file contains something we don't want
if egrep -q -v '^#|^[^ ]*=[^;]*' &quot;$configfile&quot;; then if egrep -q -v '^#|^[^ ]*=[^;]*' "$configfile"; then
echo &quot;Config file is unclean, cleaning it...&quot; >&2 echo "Config file is unclean, cleaning it..." >&2
# filter the original to a new file # filter the original to a new file
egrep '^#|^[^ ]*=[^;&]*' &quot;$configfile&quot; > &quot;$configfile_secured&quot; egrep '^#|^[^ ]*=[^;&]*' "$configfile" > "$configfile_secured"
configfile=&quot;$configfile_secured&quot; configfile="$configfile_secured"
fi fi
# now source it, either the original or the filtered variant # now source it, either the original or the filtered variant
source &quot;$configfile&quot; source "$configfile"
**<u>To make clear what it does:</u>** egrep checks if the file contains **<u>To make clear what it does:</u>** egrep checks if the file contains
something we don't want, if yes, egrep filters the file and writes the something we don't want, if yes, egrep filters the file and writes the

View File

@ -41,7 +41,7 @@ let's use the more readable, [modern](/syntax/expansion/cmdsubst) `$()`
construct instead of the old style backticks: construct instead of the old style backticks:
``` bash ``` bash
sh $ for i in *.zip; do j=$(basename &quot;$i&quot; &quot;.zip&quot;); mkdir $j; cd $j; unzip ../$i; cd ..; done sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir $j; cd $j; unzip ../$i; cd ..; done
``` ```
In Bash we don't need the subshell or the external basename command. See In Bash we don't need the subshell or the external basename command. See
@ -49,7 +49,7 @@ In Bash we don't need the subshell or the external basename command. See
expansion](/syntax/pe#substring_removal): expansion](/syntax/pe#substring_removal):
``` bash ``` bash
bash $ for i in *.zip; do j=&quot;${i%.zip}&quot;; mkdir $j; cd $j; unzip ../$i; cd ..; done bash $ for i in *.zip; do j="${i%.zip}"; mkdir $j; cd $j; unzip ../$i; cd ..; done
``` ```
Let's keep going: Let's keep going:
@ -69,7 +69,7 @@ in the previous step? Well, if you don't quote `$j`, wordsplitting can
happen again. happen again.
``` bash ``` bash
$ mkdir &quot;$j&quot; && cd &quot;$j&quot; && ... && cd .. $ mkdir "$j" && cd "$j" && ... && cd ..
``` ```
That's almost right, but there's one problem -- what happens if `$j` That's almost right, but there's one problem -- what happens if `$j`
@ -78,7 +78,7 @@ directory. That's wrong! `cd -` causes cd to return to the previous
working directory, so it's a much better choice: working directory, so it's a much better choice:
``` bash ``` bash
$ mkdir &quot;$j&quot; && cd &quot;$j&quot; && ... && cd - $ mkdir "$j" && cd "$j" && ... && cd -
``` ```
(If it occurred to you that I forgot to check for success after cd -, (If it occurred to you that I forgot to check for success after cd -,
@ -90,17 +90,17 @@ problem.)
So now we have: So now we have:
``` bash ``` bash
sh $ for i in *.zip; do j=$(basename &quot;$i&quot; &quot;.zip&quot;); mkdir &quot;$j&quot; && cd &quot;$j&quot; && unzip ../$i && cd -; done sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && cd "$j" && unzip ../$i && cd -; done
``` ```
``` bash ``` bash
bash $ for i in *.zip; do j=&quot;${i%.zip}&quot;; mkdir &quot;$j&quot; && cd &quot;$j&quot; && unzip ../$i && cd -; done bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && cd "$j" && unzip ../$i && cd -; done
``` ```
Let's throw the `unzip` command back in the mix: Let's throw the `unzip` command back in the mix:
``` bash ``` bash
mkdir &quot;$j&quot; && cd &quot;$j&quot; && unzip ../$i && cd - mkdir "$j" && cd "$j" && unzip ../$i && cd -
``` ```
Well, besides word splitting, there's nothing terribly wrong with this. Well, besides word splitting, there's nothing terribly wrong with this.
@ -110,15 +110,15 @@ implementations I've seen can do it with the -d flag. So we can drop the
cd commands entirely: cd commands entirely:
``` bash ``` bash
$ mkdir &quot;$j&quot; && unzip -d &quot;$j&quot; &quot;$i&quot; $ mkdir "$j" && unzip -d "$j" "$i"
``` ```
``` bash ``` bash
sh $ for i in *.zip; do j=$(basename &quot;$i&quot; &quot;.zip&quot;); mkdir &quot;$j&quot; && unzip -d &quot;$j&quot; &quot;$i&quot;; done sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && unzip -d "$j" "$i"; done
``` ```
``` bash ``` bash
bash $ for i in *.zip; do j=&quot;${i%.zip}&quot;; mkdir &quot;$j&quot; && unzip -d &quot;$j&quot; &quot;$i&quot;; done bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && unzip -d "$j" "$i"; done
``` ```
There! That's as good as it gets. There! That's as good as it gets.

View File

@ -38,10 +38,10 @@ command, `printf` ("help printf"). Shown here as an example Bash
function to prefix text to file content: function to prefix text to file content:
# insertHead &quot;$text&quot; &quot;$file&quot; # insertHead "$text" "$file"
insertHead() { insertHead() {
printf '%s\n' H 1i &quot;$1&quot; . w | ed -s &quot;$2&quot; printf '%s\n' H 1i "$1" . w | ed -s "$2"
} }
**<u>Here-strings</u>** **<u>Here-strings</u>**
@ -221,9 +221,9 @@ prints the result to stdout - `,p`):
To compare, here's a possible `sed` solution which must use Bash To compare, here's a possible `sed` solution which must use Bash
arithmetic and the external program `wc`: arithmetic and the external program `wc`:
sed &quot;$(($(wc -l < FILE1)-1))r FILE2&quot; FILE1 sed "$(($(wc -l < FILE1)-1))r FILE2" FILE1
# UPDATE here's one which uses GNU sed's &quot;e&quot; parameter for the s-command # UPDATE here's one which uses GNU sed's "e" parameter for the s-command
# it executes the commands found in pattern space. I'll take that as a # it executes the commands found in pattern space. I'll take that as a
# security risk, but well, sometimes GNU > security, you know... # security risk, but well, sometimes GNU > security, you know...
sed '${h;s/.*/cat FILE2/e;G}' FILE1 sed '${h;s/.*/cat FILE2/e;G}' FILE1
@ -250,10 +250,10 @@ about it with the g (global) command:
echo $'1\n1\n3' > file echo $'1\n1\n3' > file
#replace all lines matching 1 by &quot;replacement&quot; #replace all lines matching 1 by "replacement"
ed -s file <<< $'g/1/s/1/replacement/\n,p' ed -s file <<< $'g/1/s/1/replacement/\n,p'
#replace the first line matching 1 by &quot;replacement&quot; #replace the first line matching 1 by "replacement"
#(because it starts searching from the last line) #(because it starts searching from the last line)
ed -s file <<< $'s/1/replacement/\n,p' ed -s file <<< $'s/1/replacement/\n,p'
@ -276,7 +276,7 @@ If you want the same behaviour you can use g/foo/ to trick ed. g/foo/
will apply the command on all lines matching foo, thus the substitution will apply the command on all lines matching foo, thus the substitution
will succeed and ed will not produce an error when foo is not found: will succeed and ed will not produce an error when foo is not found:
#Second version will add the line with &quot;something&quot; even if foo is not found #Second version will add the line with "something" even if foo is not found
ed -s file <<< $'H\n1g/foo/s/foo/bar/\na\nsomething\n.\nw' ed -s file <<< $'H\n1g/foo/s/foo/bar/\na\nsomething\n.\nw'
In fact, even a substitution that fails after a g/ / command does not In fact, even a substitution that fails after a g/ / command does not

View File

@ -143,14 +143,14 @@ doesn't try to parse the positional parameters, but these given words.
This way, you are able to parse any option set you like, here for This way, you are able to parse any option set you like, here for
example from an array: example from an array:
while getopts :f:h opt &quot;${MY_OWN_SET[@]}&quot;; do while getopts :f:h opt "${MY_OWN_SET[@]}"; do
... ...
done done
A call to `getopts` **without** these additional arguments is A call to `getopts` **without** these additional arguments is
**equivalent** to explicitly calling it with `"$@"`: **equivalent** to explicitly calling it with `"$@"`:
getopts ... &quot;$@&quot; getopts ... "$@"
### Error Reporting ### Error Reporting
@ -189,13 +189,13 @@ preceding the whole option string with a colon (`:`):
``` bash ``` bash
#!/bin/bash #!/bin/bash
while getopts &quot;:a&quot; opt; do while getopts ":a" opt; do
case $opt in case $opt in
a) a)
echo &quot;-a was triggered!&quot; >&2 echo "-a was triggered!" >&2
;; ;;
\?) \?)
echo &quot;Invalid option: -$OPTARG&quot; >&2 echo "Invalid option: -$OPTARG" >&2
;; ;;
esac esac
done done
@ -285,17 +285,17 @@ Let's extend our example from above. Just a little bit:
``` bash ``` bash
#!/bin/bash #!/bin/bash
while getopts &quot;:a:&quot; opt; do while getopts ":a:" opt; do
case $opt in case $opt in
a) a)
echo &quot;-a was triggered, Parameter: $OPTARG&quot; >&2 echo "-a was triggered, Parameter: $OPTARG" >&2
;; ;;
\?) \?)
echo &quot;Invalid option: -$OPTARG&quot; >&2 echo "Invalid option: -$OPTARG" >&2
exit 1 exit 1
;; ;;
:) :)
echo &quot;Option -$OPTARG requires an argument.&quot; >&2 echo "Option -$OPTARG requires an argument." >&2
exit 1 exit 1
;; ;;
esac esac

View File

@ -67,9 +67,9 @@ With mkdir it seems, we have our two steps in one simple operation. A
``` bash ``` bash
if mkdir /var/lock/mylock; then if mkdir /var/lock/mylock; then
echo &quot;Locking succeeded&quot; >&2 echo "Locking succeeded" >&2
else else
echo &quot;Lock failed - exit&quot; >&2 echo "Lock failed - exit" >&2
exit 1 exit 1
fi fi
``` ```
@ -103,12 +103,12 @@ Need to write a code example here.
``` bash ``` bash
if ( set -o noclobber; echo &quot;locked&quot; > &quot;$lockfile&quot;) 2> /dev/null; then if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then
trap 'rm -f &quot;$lockfile&quot;; exit $?' INT TERM EXIT trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
echo &quot;Locking succeeded&quot; >&2 echo "Locking succeeded" >&2
rm -f &quot;$lockfile&quot; rm -f "$lockfile"
else else
echo &quot;Lock failed - exit&quot; >&2 echo "Lock failed - exit" >&2
exit 1 exit 1
fi fi
@ -136,59 +136,59 @@ the locking process is shown:
#!/bin/bash #!/bin/bash
# lock dirs/files # lock dirs/files
LOCKDIR=&quot;/tmp/statsgen-lock&quot; LOCKDIR="/tmp/statsgen-lock"
PIDFILE=&quot;${LOCKDIR}/PID&quot; PIDFILE="${LOCKDIR}/PID"
# exit codes and text # exit codes and text
ENO_SUCCESS=0; ETXT[0]=&quot;ENO_SUCCESS&quot; ENO_SUCCESS=0; ETXT[0]="ENO_SUCCESS"
ENO_GENERAL=1; ETXT[1]=&quot;ENO_GENERAL&quot; ENO_GENERAL=1; ETXT[1]="ENO_GENERAL"
ENO_LOCKFAIL=2; ETXT[2]=&quot;ENO_LOCKFAIL&quot; ENO_LOCKFAIL=2; ETXT[2]="ENO_LOCKFAIL"
ENO_RECVSIG=3; ETXT[3]=&quot;ENO_RECVSIG&quot; ENO_RECVSIG=3; ETXT[3]="ENO_RECVSIG"
### ###
### start locking attempt ### start locking attempt
### ###
trap 'ECODE=$?; echo &quot;[statsgen] Exit: ${ETXT[ECODE]}($ECODE)&quot; >&2' 0 trap 'ECODE=$?; echo "[statsgen] Exit: ${ETXT[ECODE]}($ECODE)" >&2' 0
echo -n &quot;[statsgen] Locking: &quot; >&2 echo -n "[statsgen] Locking: " >&2
if mkdir &quot;${LOCKDIR}&quot; &>/dev/null; then if mkdir "${LOCKDIR}" &>/dev/null; then
# lock succeeded, install signal handlers before storing the PID just in case # lock succeeded, install signal handlers before storing the PID just in case
# storing the PID fails # storing the PID fails
trap 'ECODE=$?; trap 'ECODE=$?;
echo &quot;[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)&quot; >&2 echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2
rm -rf &quot;${LOCKDIR}&quot;' 0 rm -rf "${LOCKDIR}"' 0
echo &quot;$$&quot; >&quot;${PIDFILE}&quot; echo "$$" >"${PIDFILE}"
# the following handler will exit the script upon receiving these signals # the following handler will exit the script upon receiving these signals
# the trap on &quot;0&quot; (EXIT) from above will be triggered by this trap's &quot;exit&quot; command! # the trap on "0" (EXIT) from above will be triggered by this trap's "exit" command!
trap 'echo &quot;[statsgen] Killed by a signal.&quot; >&2 trap 'echo "[statsgen] Killed by a signal." >&2
exit ${ENO_RECVSIG}' 1 2 3 15 exit ${ENO_RECVSIG}' 1 2 3 15
echo &quot;success, installed signal handlers&quot; echo "success, installed signal handlers"
else else
# lock failed, check if the other PID is alive # lock failed, check if the other PID is alive
OTHERPID=&quot;$(cat &quot;${PIDFILE}&quot;)&quot; OTHERPID="$(cat "${PIDFILE}")"
# if cat isn't able to read the file, another instance is probably # if cat isn't able to read the file, another instance is probably
# about to remove the lock -- exit, we're *still* locked # about to remove the lock -- exit, we're *still* locked
# Thanks to Grzegorz Wierzowiecki for pointing out this race condition on # Thanks to Grzegorz Wierzowiecki for pointing out this race condition on
# http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash # http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash
if [ $? != 0 ]; then if [ $? != 0 ]; then
echo &quot;lock failed, PID ${OTHERPID} is active&quot; >&2 echo "lock failed, PID ${OTHERPID} is active" >&2
exit ${ENO_LOCKFAIL} exit ${ENO_LOCKFAIL}
fi fi
if ! kill -0 $OTHERPID &>/dev/null; then if ! kill -0 $OTHERPID &>/dev/null; then
# lock is stale, remove it and restart # lock is stale, remove it and restart
echo &quot;removing stale lock of nonexistant PID ${OTHERPID}&quot; >&2 echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2
rm -rf &quot;${LOCKDIR}&quot; rm -rf "${LOCKDIR}"
echo &quot;[statsgen] restarting myself&quot; >&2 echo "[statsgen] restarting myself" >&2
exec &quot;$0&quot; &quot;$@&quot; exec "$0" "$@"
else else
# lock is valid and OTHERPID is active - exit, we're locked! # lock is valid and OTHERPID is active - exit, we're locked!
echo &quot;lock failed, PID ${OTHERPID} is active&quot; >&2 echo "lock failed, PID ${OTHERPID} is active" >&2
exit ${ENO_LOCKFAIL} exit ${ENO_LOCKFAIL}
fi fi

View File

@ -183,7 +183,7 @@ give the `-v` option:
You can extract all files, or files (not) matching specific patterns You can extract all files, or files (not) matching specific patterns
from an archive using constructs like: from an archive using constructs like:
# &quot;normal&quot; extraction # "normal" extraction
pax -rf myarchive.tar '*.txt' pax -rf myarchive.tar '*.txt'
# with inverted pattern # with inverted pattern
@ -201,8 +201,8 @@ command, use:
To copy directory contents to another directory on a remote system, use: To copy directory contents to another directory on a remote system, use:
pax -w localdir | ssh user@host &quot;cd distantdest && pax -r -v&quot; pax -w localdir | ssh user@host "cd distantdest && pax -r -v"
pax -w localdir | gzip | ssh user@host &quot;cd distantdir && gunzip | pax -r -v&quot; #compress the sent data pax -w localdir | gzip | ssh user@host "cd distantdir && gunzip | pax -r -v" #compress the sent data
These commands create a copy of localdir in distandir (distantdir/dir) These commands create a copy of localdir in distandir (distantdir/dir)
on the remote machine. on the remote machine.

View File

@ -359,12 +359,12 @@ the beginning of your script.
Let's see another use case. We want to read a file line by line, this is Let's see another use case. We want to read a file line by line, this is
easy, we just do: easy, we just do:
while read -r line;do echo &quot;$line&quot;;done < file while read -r line;do echo "$line";done < file
Now, we want, after printing each line, to do a pause, waiting for the Now, we want, after printing each line, to do a pause, waiting for the
user to press a key: user to press a key:
while read -r line;do echo &quot;$line&quot;; read -p &quot;Press any key&quot; -n 1;done < file while read -r line;do echo "$line"; read -p "Press any key" -n 1;done < file
And, surprise this doesn't work. Why? because the shell descriptor of And, surprise this doesn't work. Why? because the shell descriptor of
the while loop looks like: the while loop looks like:
@ -390,7 +390,7 @@ descriptor from which `read` should read. Cool. Now let's use `exec` to
get another descriptor: get another descriptor:
exec 3<file exec 3<file
while read -u 3 line;do echo &quot;$line&quot;; read -p &quot;Press any key&quot; -n 1;done while read -u 3 line;do echo "$line"; read -p "Press any key" -n 1;done
Now the file descriptors look like: Now the file descriptors look like:
@ -656,7 +656,7 @@ recommendations:
# Good! # Good!
{ cmd1 <<<'my input'; cmd2; } >someFile { cmd1 <<<'my input'; cmd2; } >someFile
# Bad. Is the &quot;1&quot; a file descriptor or an argument to cmd? (answer: it's the FD). Is the space after the herestring part of the input data? (answer: No). # Bad. Is the "1" a file descriptor or an argument to cmd? (answer: it's the FD). Is the space after the herestring part of the input data? (answer: No).
# The redirects are also not delimited in any obvious way. # The redirects are also not delimited in any obvious way.
cmd 2>& 1 <<< stuff cmd 2>& 1 <<< stuff

View File

@ -9,18 +9,18 @@ We have a simple **stat.sh** script:
#!/usr/bin/env bash #!/usr/bin/env bash
if [ -z &quot;$1&quot; ] if [ -z "$1" ]
then then
DIR=./ DIR=./
else else
DIR=$1 DIR=$1
fi fi
echo &quot;Evaluate *.py statistics&quot; echo "Evaluate *.py statistics"
FILES=$(find $DIR -name '*.py' | wc -l) FILES=$(find $DIR -name '*.py' | wc -l)
LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l) LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l)
echo &quot;PYTHON FILES: $FILES&quot; echo "PYTHON FILES: $FILES"
echo &quot;PYTHON LINES: $LINES&quot; echo "PYTHON LINES: $LINES"
This script evaluate the number of python files and the number of python This script evaluate the number of python files and the number of python
code lines in the files. We can use it like **./stat.sh \<dir\>** code lines in the files. We can use it like **./stat.sh \<dir\>**

View File

@ -17,7 +17,7 @@ From `builtins/read.def`:
``` C ``` C
/* If there are no variables, save the text of the line read to the /* If there are no variables, save the text of the line read to the
variable $REPLY. ksh93 strips leading and trailing IFS whitespace, variable $REPLY. ksh93 strips leading and trailing IFS whitespace,
so that `read x ; echo &quot;$x&quot;' and `read ; echo &quot;$REPLY&quot;' behave the so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
same way, but I believe that the difference in behaviors is useful same way, but I believe that the difference in behaviors is useful
enough to not do it. Without the bash behavior, there is no way enough to not do it. Without the bash behavior, there is no way
to read a line completely without interpretation or modification to read a line completely without interpretation or modification

View File

@ -249,7 +249,7 @@ For this topic, see also
| `${PARAMETER//PATTERN/REPLACEMENT}` | 2.0 | | | `${PARAMETER//PATTERN/REPLACEMENT}` | 2.0 | |
| `${PARAMETER:OFFSET:LENGTH}` | 2.0 | | | `${PARAMETER:OFFSET:LENGTH}` | 2.0 | |
| `${!PARAMETER}` (indirection) | 2.0 | | | `${!PARAMETER}` (indirection) | 2.0 | |
| `$&quot;...&quot;` (localized strings) | 2.0 | | | `$"..."` (localized strings) | 2.0 | |
| `$'...'` (ANSI-C-like strings) | 2.0 | | | `$'...'` (ANSI-C-like strings) | 2.0 | |
| `\xNNN` in `$'...'` (and `echo -e`) | 2.02-alpha1 | | | `\xNNN` in `$'...'` (and `echo -e`) | 2.02-alpha1 | |
| `$(< FILENAME)` (file content) | 2.02-alpha1 | | | `$(< FILENAME)` (file content) | 2.02-alpha1 | |

View File

@ -27,7 +27,7 @@ The in-file specification of the interpreter of that file, for example:
``` bash ``` bash
#!/bin/bash #!/bin/bash
echo &quot;Hello world...&quot; echo "Hello world..."
``` ```
This is interpreted by the kernel [^1] of your system. In general, if a This is interpreted by the kernel [^1] of your system. In general, if a
@ -164,9 +164,9 @@ specific user is present in /etc/passwd:
``` bash ``` bash
if grep ^root /etc/passwd; then if grep ^root /etc/passwd; then
echo &quot;The user root was found&quot; echo "The user root was found"
else else
echo &quot;The user root was not found&quot; echo "The user root was not found"
fi fi
``` ```
@ -176,10 +176,10 @@ brackets are not part of the shell syntax, the left bracket **is** the
test command! test command!
``` bash ``` bash
if [ &quot;$mystring&quot; = &quot;Hello world&quot; ]; then if [ "$mystring" = "Hello world" ]; then
echo &quot;Yeah dude, you entered the right words...&quot; echo "Yeah dude, you entered the right words..."
else else
echo &quot;Eeeek - go away...&quot; echo "Eeeek - go away..."
fi fi
``` ```
@ -190,8 +190,8 @@ This lets you execute a command based on whether or not the previous
command completed successfully: command completed successfully:
``` bash ``` bash
grep ^root: /etc/passwd >/dev/null || echo &quot;root was not found - check the pub at the corner.&quot; grep ^root: /etc/passwd >/dev/null || echo "root was not found - check the pub at the corner."
which vi && echo &quot;Your favourite editor is installed.&quot; which vi && echo "Your favourite editor is installed."
``` ```
Please, when your script exits on errors, provide a "FALSE" exit code, Please, when your script exits on errors, provide a "FALSE" exit code,
@ -206,7 +206,7 @@ can help with debugging or tests. Comments start with the \# character
``` bash ``` bash
#!/bin/bash #!/bin/bash
# This is a small script to say something. # This is a small script to say something.
echo &quot;Be liberal in what you accept, and conservative in what you send&quot; # say something echo "Be liberal in what you accept, and conservative in what you send" # say something
``` ```
The first thing was already explained, it's the so-called shebang, for The first thing was already explained, it's the so-called shebang, for
@ -227,14 +227,14 @@ a shutdown:
``` bash ``` bash
#!/bin/bash #!/bin/bash
# Write info mails, do some tasks and bring down the system in a safe way # Write info mails, do some tasks and bring down the system in a safe way
echo &quot;System halt requested&quot; | mail -s &quot;System halt&quot; netadmin@example.com echo "System halt requested" | mail -s "System halt" netadmin@example.com
logger -t SYSHALT &quot;System halt requested&quot; logger -t SYSHALT "System halt requested"
##### The following &quot;code block&quot; is effectively ignored ##### The following "code block" is effectively ignored
: <<&quot;SOMEWORD&quot; : <<"SOMEWORD"
/etc/init.d/mydatabase clean_stop /etc/init.d/mydatabase clean_stop
mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1 mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1
logger -t SYSHALT &quot;System halt: pre-shutdown actions done, now shutting down the system&quot; logger -t SYSHALT "System halt: pre-shutdown actions done, now shutting down the system"
shutdown -h NOW shutdown -h NOW
SOMEWORD SOMEWORD
##### The ignored codeblock ends here ##### The ignored codeblock ends here
@ -312,13 +312,13 @@ echo $foo
} }
# this will print &quot;external&quot; # this will print "external"
echo $foo echo $foo
# this will print &quot;internal&quot; # this will print "internal"
printvalue printvalue
# this will print - again - &quot;external&quot; # this will print - again - "external"
echo $foo echo $foo
``` ```
@ -341,10 +341,10 @@ A variable can be tagged to be part of the environment using the
``` bash ``` bash
# create a new variable and set it: # create a new variable and set it:
# -> This is a normal shell variable, not an environment variable! # -> This is a normal shell variable, not an environment variable!
myvariable=&quot;Hello world.&quot; myvariable="Hello world."
# make the variable visible to all child processes: # make the variable visible to all child processes:
# -> Make it an environment variable: &quot;export&quot; it # -> Make it an environment variable: "export" it
export myvariable export myvariable
``` ```

View File

@ -52,20 +52,20 @@ manpage](http://unixhelp.ed.ac.uk/CGI/man-cgi?logger+1)).
Insert **echos** everywhere you can, and print to `stderr`: Insert **echos** everywhere you can, and print to `stderr`:
echo &quot;DEBUG: current i=$i&quot; >&2 echo "DEBUG: current i=$i" >&2
If you read input from **anywhere**, such as a file or [command If you read input from **anywhere**, such as a file or [command
substitution](/syntax/expansion/cmdsubst), print the debug output with substitution](/syntax/expansion/cmdsubst), print the debug output with
literal quotes, to see leading and trailing spaces! literal quotes, to see leading and trailing spaces!
pid=$(< fooservice.pid) pid=$(< fooservice.pid)
echo &quot;DEBUG: read from file: pid=\&quot;$pid\&quot;&quot; >&2 echo "DEBUG: read from file: pid=\"$pid\"" >&2
Bash's [printf](/commands/builtin/printf) command has the `%q` format, Bash's [printf](/commands/builtin/printf) command has the `%q` format,
which is handy for verifying whether strings are what they appear to be. which is handy for verifying whether strings are what they appear to be.
foo=$(< inputfile) foo=$(< inputfile)
printf &quot;DEBUG: foo is |%q|\n&quot; &quot;$foo&quot; >&2 printf "DEBUG: foo is |%q|\n" "$foo" >&2
# exposes whitespace (such as CRs, see below) and non-printing characters # exposes whitespace (such as CRs, see below) and non-printing characters
## Use shell debug output ## Use shell debug output
@ -100,7 +100,7 @@ Here's a simple command (a string comparison using the [classic test
command](/commands/classictest)) executed while in `set -x` mode: command](/commands/classictest)) executed while in `set -x` mode:
set -x set -x
foo=&quot;bar baz&quot; foo="bar baz"
[ $foo = test ] [ $foo = test ]
That fails. Why? Let's see the `xtrace` output: That fails. Why? Let's see the `xtrace` output:
@ -112,7 +112,7 @@ words (which you would have realized if you READ THE ERROR MESSAGES ;)
). Let's check it... ). Let's check it...
# next try # next try
[ &quot;$foo&quot; = test ] [ "$foo" = test ]
`xtrace` now gives `xtrace` now gives
@ -157,10 +157,10 @@ For general debugging purposes you can also define a function and a
variable to use: variable to use:
debugme() { debugme() {
[[ $script_debug = 1 ]] && &quot;$@&quot; || : [[ $script_debug = 1 ]] && "$@" || :
# be sure to append || : or || true here or use return 0, since the return code # be sure to append || : or || true here or use return 0, since the return code
# of this function should always be 0 to not influence anything else with an unwanted # of this function should always be 0 to not influence anything else with an unwanted
# &quot;false&quot; return code (for example the script's exit code if this function is used # "false" return code (for example the script's exit code if this function is used
# as the very last command in the script) # as the very last command in the script)
} }
@ -171,9 +171,9 @@ Use it like this:
script_debug=1 script_debug=1
# to turn it off, set script_debug=0 # to turn it off, set script_debug=0
debugme logger &quot;Sorting the database&quot; debugme logger "Sorting the database"
database_sort database_sort
debugme logger &quot;Finished sorting the database, exit code $?&quot; debugme logger "Finished sorting the database, exit code $?"
Of course this can be used to execute something other than echo during Of course this can be used to execute something other than echo during
debugging: debugging:
@ -236,7 +236,7 @@ to be deliberate. Bash 4.0 added an extra message for this:
### Unexpected end of file while looking for matching ... ### Unexpected end of file while looking for matching ...
script.sh: line 50: unexpected EOF while looking for matching `&quot;' script.sh: line 50: unexpected EOF while looking for matching `"'
script.sh: line 100: syntax error: unexpected end of file script.sh: line 100: syntax error: unexpected end of file
This one indicates the double-quote opened in line 50 does not have a This one indicates the double-quote opened in line 50 does not have a
@ -259,8 +259,8 @@ test-command that yielded the error.
### !": event not found ### !": event not found
$ echo &quot;Hello world!&quot; $ echo "Hello world!"
bash: !&quot;: event not found bash: !": event not found
This is not an error per se. It happens in interactive shells, when the This is not an error per se. It happens in interactive shells, when the
C-Shell-styled history expansion ("`!searchword`") is enabled. This is C-Shell-styled history expansion ("`!searchword`") is enabled. This is
@ -275,7 +275,7 @@ the default. Disable it like this:
When this happens during a script **function definition** or on the When this happens during a script **function definition** or on the
commandline, e.g. commandline, e.g.
$ foo () { echo &quot;Hello world&quot;; } $ foo () { echo "Hello world"; }
bash: syntax error near unexpected token `(' bash: syntax error near unexpected token `('
you most likely have an alias defined with the same name as the function you most likely have an alias defined with the same name as the function
@ -323,7 +323,7 @@ carriage return character!):
#!/bin/bash^M #!/bin/bash^M
^M ^M
echo &quot;Hello world&quot;^M echo "Hello world"^M
... ...
Here's what happens because of the `#!/bin/bash^M` in our shebang: Here's what happens because of the `#!/bin/bash^M` in our shebang:

View File

@ -85,13 +85,13 @@ There is no `$` (dollar-sign) when you reference the **name** of a
variable! Bash is not PHP! variable! Bash is not PHP!
# THIS IS WRONG! # THIS IS WRONG!
$myvar=&quot;Hello world!&quot; $myvar="Hello world!"
A variable name preceeded with a dollar-sign always means that the A variable name preceeded with a dollar-sign always means that the
variable gets **expanded**. In the example above, it might expand to variable gets **expanded**. In the example above, it might expand to
nothing (because it wasn't set), effectively resulting in... nothing (because it wasn't set), effectively resulting in...
=&quot;Hello world!&quot; ="Hello world!"
...which **definitely is wrong**! ...which **definitely is wrong**!
@ -129,7 +129,7 @@ assigned value**:
example=Hello example=Hello
# CORRECT 2 # CORRECT 2
example=&quot; Hello&quot; example=" Hello"
### Expanding (using) variables ### Expanding (using) variables
@ -142,7 +142,7 @@ result isn't always the same.
Let's define an example variable containing text with spaces: Let's define an example variable containing text with spaces:
example=&quot;Hello world&quot; example="Hello world"
| Used form | result | number of words | | Used form | result | number of words |
|--------------|---------------|-----------------| |--------------|---------------|-----------------|
@ -155,10 +155,10 @@ If you use parameter expansion, you **must** use the **name** (`PATH`)
of the referenced variables/parameters. i.e. **not** (`$PATH`): of the referenced variables/parameters. i.e. **not** (`$PATH`):
# WRONG! # WRONG!
echo &quot;The first character of PATH is ${$PATH:0:1}&quot; echo "The first character of PATH is ${$PATH:0:1}"
# CORRECT # CORRECT
echo &quot;The first character of PATH is ${PATH:0:1}&quot; echo "The first character of PATH is ${PATH:0:1}"
Note that if you are using variables in [arithmetic Note that if you are using variables in [arithmetic
expressions](/syntax/arith_expr), then the bare **name** is allowed: expressions](/syntax/arith_expr), then the bare **name** is allowed:
@ -217,7 +217,7 @@ value, you **don't need** to use `$?` in a test command like this:
grep ^root: /etc/passwd >/dev/null 2>&1 grep ^root: /etc/passwd >/dev/null 2>&1
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo &quot;root was not found - check the pub at the corner&quot; echo "root was not found - check the pub at the corner"
fi fi
``` ```
@ -225,14 +225,14 @@ This can be simplified to:
``` bash ``` bash
if ! grep ^root: /etc/passwd >/dev/null 2>&1; then if ! grep ^root: /etc/passwd >/dev/null 2>&1; then
echo &quot;root was not found - check the pub at the corner&quot; echo "root was not found - check the pub at the corner"
fi fi
``` ```
Or, simpler yet: Or, simpler yet:
``` bash ``` bash
grep ^root: /etc/passwd >/dev/null 2>&1 || echo &quot;root was not found - check the pub at the corner&quot; grep ^root: /etc/passwd >/dev/null 2>&1 || echo "root was not found - check the pub at the corner"
``` ```
If you need the specific value of `$?`, there's no other choice. But if If you need the specific value of `$?`, there's no other choice. But if
@ -252,7 +252,7 @@ When you want to run a command (or a pipeline) and save (or print) the
syntax: syntax:
$(ls -l /tmp) $(ls -l /tmp)
newvariable=$(printf &quot;foo&quot;) newvariable=$(printf "foo")
When you want to use the **return value** of a command, just use the When you want to use the **return value** of a command, just use the
command, or add ( ) to run a command or pipeline in a subshell: command, or add ( ) to run a command or pipeline in a subshell:

View File

@ -134,7 +134,7 @@ exit code, a 100% equivalent construct would be:
... ...
# portable equivalent command # portable equivalent command
if [ &quot;$((MATH))&quot; -ne 0 ]; then if [ "$((MATH))" -ne 0 ]; then
... ...
Quotes around the arithmetic expansion `$((MATH))` should not be Quotes around the arithmetic expansion `$((MATH))` should not be
@ -191,10 +191,10 @@ awk) or 'nawk'. 'oawk' (i.e. Original/Old awk) does not have the rand()
or srand() functions, so is best avoided. or srand() functions, so is best avoided.
# 'gawk' can produce random numbers using srand(). In this example, 10 integers between 1 and 500: # 'gawk' can produce random numbers using srand(). In this example, 10 integers between 1 and 500:
randpm=$(gawk -v min=1 -v max=500 -v nNum=10 'BEGIN { srand(systime() + PROCINFO[&quot;pid&quot;]); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }') randpm=$(gawk -v min=1 -v max=500 -v nNum=10 'BEGIN { srand(systime() + PROCINFO["pid"]); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
# 'nawk' and 'mawk' does the same, but needs a seed to be provided for its rand() function. In this example we use $(date) # 'nawk' and 'mawk' does the same, but needs a seed to be provided for its rand() function. In this example we use $(date)
randpm=$(mawk -v min=1 -v max=500 -v nNum=10 -v seed=&quot;$(date +%Y%M%d%H%M%S)&quot; 'BEGIN { srand(seed); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }') randpm=$(mawk -v min=1 -v max=500 -v nNum=10 -v seed="$(date +%Y%M%d%H%M%S)" 'BEGIN { srand(seed); for (i = 0; i < nNum; ++i) {print int(min + rand() * (max - min)} }')
*Yes, I'm not an `awk` expert, so please correct it, rather than *Yes, I'm not an `awk` expert, so please correct it, rather than
complaining about possible stupid code!* complaining about possible stupid code!*
@ -232,14 +232,14 @@ For example, to check if the command `ls` is available in a location
accessible by `PATH`: accessible by `PATH`:
if hash ls >/dev/null 2>&1; then if hash ls >/dev/null 2>&1; then
echo &quot;ls is available&quot; echo "ls is available"
fi fi
Somewhat of a mass-check: Somewhat of a mass-check:
for name in ls grep sed awk; do for name in ls grep sed awk; do
if ! hash &quot;$name&quot; >/dev/null 2>&1; then if ! hash "$name" >/dev/null 2>&1; then
echo &quot;FAIL: Missing command '$name'&quot; echo "FAIL: Missing command '$name'"
exit 1 exit 1
fi fi
done done
@ -257,7 +257,7 @@ For example, to check if the command `sed` is available in a location
accessible by `PATH`: accessible by `PATH`:
if command -v sed >/dev/null 2>&1; then if command -v sed >/dev/null 2>&1; then
echo &quot;sed is available&quot; echo "sed is available"
fi fi
[^1]: "portable" doesn't necessarily mean it's POSIX, it can also mean [^1]: "portable" doesn't necessarily mean it's POSIX, it can also mean

View File

@ -35,7 +35,7 @@ shell initialization:
<u>Testscript</u> - it just echos `$0`: <u>Testscript</u> - it just echos `$0`:
#!/bin/bash #!/bin/bash
echo &quot;$0&quot; echo "$0"
You see, `$0` is always set to the name the script is called with (`>` You see, `$0` is always set to the name the script is called with (`>`
is the prompt...): is the prompt...):
@ -48,7 +48,7 @@ is the prompt...):
However, this isn't true for login shells: However, this isn't true for login shells:
> echo &quot;$0&quot; > echo "$0"
-bash -bash
In other terms, `$0` is not a positional parameter, it's a special In other terms, `$0` is not a positional parameter, it's a special
@ -85,12 +85,12 @@ go.
One way is to access specific parameters: One way is to access specific parameters:
#!/bin/bash #!/bin/bash
echo &quot;Total number of arguments: $#&quot; echo "Total number of arguments: $#"
echo &quot;Argument 1: $1&quot; echo "Argument 1: $1"
echo &quot;Argument 2: $2&quot; echo "Argument 2: $2"
echo &quot;Argument 3: $3&quot; echo "Argument 3: $3"
echo &quot;Argument 4: $4&quot; echo "Argument 4: $4"
echo &quot;Argument 5: $5&quot; echo "Argument 5: $5"
While useful in another situation, this way is lacks flexibility. The While useful in another situation, this way is lacks flexibility. The
maximum number of arguments is a fixedvalue - which is a bad idea if you maximum number of arguments is a fixedvalue - which is a bad idea if you
@ -111,7 +111,7 @@ argument list:
numargs=$# numargs=$#
for ((i=1 ; i <= numargs ; i++)) for ((i=1 ; i <= numargs ; i++))
do do
echo &quot;$1&quot; echo "$1"
shift shift
done done
@ -126,7 +126,7 @@ a given wordlist. The loop uses the positional parameters as a wordlist:
for arg for arg
do do
echo &quot;$arg&quot; echo "$arg"
done done
<u>Advantage:</u> The positional parameters will be preserved <u>Advantage:</u> The positional parameters will be preserved
@ -137,9 +137,9 @@ The next method is similar to the first example (the `for` loop), but it
doesn't test for reaching `$#`. It shifts and checks if `$1` still doesn't test for reaching `$#`. It shifts and checks if `$1` still
expands to something, using the [test command](/commands/classictest): expands to something, using the [test command](/commands/classictest):
while [ &quot;$1&quot; ] while [ "$1" ]
do do
echo &quot;$1&quot; echo "$1"
shift shift
done done
@ -148,8 +148,8 @@ Looks nice, but has the disadvantage of stopping when `$1` is empty
may be null), using [parameter expansion for an alternate may be null), using [parameter expansion for an alternate
value](/syntax/pe#use_an_alternate_value): value](/syntax/pe#use_an_alternate_value):
while [ &quot;${1+defined}&quot; ]; do while [ "${1+defined}" ]; do
echo &quot;$1&quot; echo "$1"
shift shift
done done
@ -226,7 +226,7 @@ decremented.
<u>**Example:**</u> START at the last positional parameter: <u>**Example:**</u> START at the last positional parameter:
echo &quot;${@: -1}&quot; echo "${@: -1}"
<u>**Attention**</u>: As of Bash 4, a `START` of `0` includes the <u>**Attention**</u>: As of Bash 4, a `START` of `0` includes the
special parameter `$0`, i.e. the shell name or whatever \$0 is set to, special parameter `$0`, i.e. the shell name or whatever \$0 is set to,
@ -240,7 +240,7 @@ only way to set them. The [builtin command, set](/commands/builtin/set)
may be used to "artificially" change the positional parameters from may be used to "artificially" change the positional parameters from
inside the script or function: inside the script or function:
set &quot;This is&quot; my new &quot;set of&quot; positional parameters set "This is" my new "set of" positional parameters
# RESULTS IN # RESULTS IN
# $1: This is # $1: This is
@ -282,9 +282,9 @@ rudimentary way to parse your arguments.
while : while :
do do
case &quot;$1&quot; in case "$1" in
-f | --file) -f | --file)
file=&quot;$2&quot; # You may want to check validity of $2 file="$2" # You may want to check validity of $2
shift 2 shift 2
;; ;;
-h | --help) -h | --help)
@ -293,31 +293,31 @@ rudimentary way to parse your arguments.
exit 0 exit 0
;; ;;
-u | --user) -u | --user)
username=&quot;$2&quot; # You may want to check validity of $2 username="$2" # You may want to check validity of $2
shift 2 shift 2
;; ;;
-v | --verbose) -v | --verbose)
# It's better to assign a string, than a number like &quot;verbose=1&quot; # It's better to assign a string, than a number like "verbose=1"
# because if you're debugging the script with &quot;bash -x&quot; code like this: # because if you're debugging the script with "bash -x" code like this:
# #
# if [ &quot;$verbose&quot; ] ... # if [ "$verbose" ] ...
# #
# You will see: # You will see:
# #
# if [ &quot;verbose&quot; ] ... # if [ "verbose" ] ...
# #
# Instead of cryptic # Instead of cryptic
# #
# if [ &quot;1&quot; ] ... # if [ "1" ] ...
# #
verbose=&quot;verbose&quot; verbose="verbose"
shift shift
;; ;;
--) # End of all options --) # End of all options
shift shift
break; break;
-*) -*)
echo &quot;Error: Unknown option: $1&quot; >&2 echo "Error: Unknown option: $1" >&2
exit 1 exit 1
;; ;;
*) # No more options *) # No more options
@ -346,7 +346,7 @@ options" for `ls` and doesn't change anything after it:
while [[ $1 ]] while [[ $1 ]]
do do
if ! ((eoo)); then if ! ((eoo)); then
case &quot;$1&quot; in case "$1" in
-a) -a)
shift shift
;; ;;
@ -354,29 +354,29 @@ options" for `ls` and doesn't change anything after it:
shift shift
;; ;;
-[^-]*a*|-a?*) -[^-]*a*|-a?*)
options+=(&quot;${1//a}&quot;) options+=("${1//a}")
shift shift
;; ;;
--) --)
eoo=1 eoo=1
options+=(&quot;$1&quot;) options+=("$1")
shift shift
;; ;;
*) *)
options+=(&quot;$1&quot;) options+=("$1")
shift shift
;; ;;
esac esac
else else
options+=(&quot;$1&quot;) options+=("$1")
# Another (worse) way of doing the same thing: # Another (worse) way of doing the same thing:
# options=(&quot;${options[@]}&quot; &quot;$1&quot;) # options=("${options[@]}" "$1")
shift shift
fi fi
done done
/bin/ls &quot;${options[@]}&quot; /bin/ls "${options[@]}"
### Using getopts ### Using getopts

View File

@ -96,7 +96,7 @@ into a variable. We run it in a loop here to count input lines:
counter=0 counter=0
cat /etc/passwd | while read; do ((counter++)); done cat /etc/passwd | while read; do ((counter++)); done
echo &quot;Lines: $counter&quot; echo "Lines: $counter"
What? It's 0? Yes! The number of lines might not be 0, but the variable What? It's 0? Yes! The number of lines might not be 0, but the variable
`$counter` still is 0. Why? Remember the diagram from above? Rewriting `$counter` still is 0. Why? Remember the diagram from above? Rewriting
@ -119,7 +119,7 @@ counter must be the "main shell". For example:
counter=0 counter=0
while read; do ((counter++)); done </etc/passwd while read; do ((counter++)); done </etc/passwd
echo &quot;Lines: $counter&quot; echo "Lines: $counter"
It's nearly self-explanatory. The `while` loop runs in the **current It's nearly self-explanatory. The `while` loop runs in the **current
shell**, the counter is incremented in the **current shell**, everything shell**, the counter is incremented in the **current shell**, everything

View File

@ -114,16 +114,16 @@ In general, every new "layer" gets a new indentation level:
case $input in case $input in
hello) hello)
echo &quot;You said hello&quot; echo "You said hello"
;; ;;
bye) bye)
echo &quot;You said bye&quot; echo "You said bye"
if foo; then if foo; then
bar bar
fi fi
;; ;;
*) *)
echo &quot;You said something weird...&quot; echo "You said something weird..."
;; ;;
esac esac
@ -172,8 +172,8 @@ loop counting variables, etc., ... (in the example: `file`)
# the prefix 'MY_' # the prefix 'MY_'
MY_LOG_DIRECTORY=/var/adm/ MY_LOG_DIRECTORY=/var/adm/
for file in &quot;$MY_LOG_DIRECTORY&quot;/*; do for file in "$MY_LOG_DIRECTORY"/*; do
echo &quot;Found Logfile: $file&quot; echo "Found Logfile: $file"
done done
### Variable initialization ### Variable initialization
@ -190,7 +190,7 @@ via the environment.
The solution is simple and effective: **Initialize them** The solution is simple and effective: **Initialize them**
my_input=&quot;&quot; my_input=""
my_array=() my_array=()
my_number=0 my_number=0
@ -216,7 +216,7 @@ expansion, you'll be safe.
If you need to parse a parameter as a list of words, you can't quote, of If you need to parse a parameter as a list of words, you can't quote, of
course, e.g. course, e.g.
list=&quot;one two three&quot; list="one two three"
# you MUST NOT quote $list here # you MUST NOT quote $list here
for word in $list; do for word in $list; do
@ -340,18 +340,18 @@ missing.
Example: Example:
my_needed_commands=&quot;sed awk lsof who&quot; my_needed_commands="sed awk lsof who"
missing_counter=0 missing_counter=0
for needed_command in $my_needed_commands; do for needed_command in $my_needed_commands; do
if ! hash &quot;$needed_command&quot; >/dev/null 2>&1; then if ! hash "$needed_command" >/dev/null 2>&1; then
printf &quot;Command not found in PATH: %s\n&quot; &quot;$needed_command&quot; >&2 printf "Command not found in PATH: %s\n" "$needed_command" >&2
((missing_counter++)) ((missing_counter++))
fi fi
done done
if ((missing_counter > 0)); then if ((missing_counter > 0)); then
printf &quot;Minimum %d commands are missing in PATH, aborting\n&quot; &quot;$missing_counter&quot; >&2 printf "Minimum %d commands are missing in PATH, aborting\n" "$missing_counter" >&2
exit 1 exit 1
fi fi

View File

@ -218,9 +218,9 @@ done by the following commands:
tput smcup tput smcup
clear clear
# example &quot;application&quot; follows... # example "application" follows...
read -n1 -p &quot;Press any key to continue...&quot; read -n1 -p "Press any key to continue..."
# example &quot;application&quot; ends here # example "application" ends here
# restore # restore
tput rmcup tput rmcup
@ -281,14 +281,14 @@ switch to, to get the other 8 colors.
<u>Directly inside the echo:</u> <u>Directly inside the echo:</u>
echo &quot;TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database.&quot; echo "TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database."
<u>With preset variables:</u> <u>With preset variables:</u>
COL_NORM=&quot;$(tput setaf 9)&quot; COL_NORM="$(tput setaf 9)"
COL_RED=&quot;$(tput setaf 1)&quot; COL_RED="$(tput setaf 1)"
COL_GREEN=&quot;$(tput setaf 2)&quot; COL_GREEN="$(tput setaf 2)"
echo &quot;It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?&quot; echo "It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?"
### Misc ### Misc
@ -303,11 +303,11 @@ switch to, to get the other 8 colors.
#!/bin/bash #!/bin/bash
DATA[0]=&quot; _/ _/ _/ _/ &quot; DATA[0]=" _/ _/ _/ _/ "
DATA[1]=&quot; _/_/_/_/_/ _/_/_/ _/_/_/ _/_/_/ _/_/_/ &quot; DATA[1]=" _/_/_/_/_/ _/_/_/ _/_/_/ _/_/_/ _/_/_/ "
DATA[2]=&quot; _/ _/ _/ _/ _/ _/ _/_/ _/ _/&quot; DATA[2]=" _/ _/ _/ _/ _/ _/ _/_/ _/ _/"
DATA[3]=&quot;_/_/_/_/_/ _/ _/ _/ _/ _/_/ _/ _/ &quot; DATA[3]="_/_/_/_/_/ _/ _/ _/ _/ _/_/ _/ _/ "
DATA[4]=&quot; _/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ &quot; DATA[4]=" _/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ "
# virtual coordinate system is X*Y ${#DATA} * 5 # virtual coordinate system is X*Y ${#DATA} * 5
@ -392,7 +392,7 @@ default), but uses only features that don't make the Bash parser crash.
} }
function colorBox { function colorBox {
(($1==lastclr)) || printf %s &quot;${colrs[lastclr=$1]:=$(tput setaf &quot;$1&quot;)}&quot; (($1==lastclr)) || printf %s "${colrs[lastclr=$1]:=$(tput setaf "$1")}"
printf '\u2588' printf '\u2588'
} }

View File

@ -48,7 +48,7 @@ as a decimal value, you need to expand the parameter and specify base
# this is interpreted as an octal: # this is interpreted as an octal:
echo $(( x )) echo $(( x ))
# this is an invalid digit for base 10 (the &quot;x&quot;)...: # this is an invalid digit for base 10 (the "x")...:
echo $(( 10#x )) echo $(( 10#x ))
## Different bases ## Different bases
@ -88,7 +88,7 @@ mean:
42 42
$ echo $((43#H)) $ echo $((43#H))
bash: 43#H: value too great for base (error token is &quot;43#H&quot;) bash: 43#H: value too great for base (error token is "43#H")
If you have no clue what a base is and why there might be other bases, If you have no clue what a base is and why there might be other bases,
and what numbers are and how they are built, then you don't need and what numbers are and how they are built, then you don't need
@ -111,7 +111,7 @@ named parameters, e.g.:
string=3 string=3
echo $((test)) echo $((test))
# will output &quot;3&quot;! # will output "3"!
Of course, in the end, when it finally evaluates to something that is Of course, in the end, when it finally evaluates to something that is
**not** a valid arithmetic expression (newlines, ordinary text, ...) **not** a valid arithmetic expression (newlines, ordinary text, ...)
@ -141,9 +141,9 @@ arithmetic expression to match the "truth" of command exit codes:
That means, the following `if`-clause will execute the `else`-thread: That means, the following `if`-clause will execute the `else`-thread:
if ((0)); then if ((0)); then
echo &quot;true&quot; echo "true"
else else
echo &quot;false&quot; echo "false"
fi fi
## Operators ## Operators
@ -318,9 +318,9 @@ and all the others, including `set -e` for autoexit on error:
MY_TEST_FLAG=0 MY_TEST_FLAG=0
if ((MY_TEST_FLAG)); then if ((MY_TEST_FLAG)); then
echo &quot;MY_TEST_FLAG is ON&quot; echo "MY_TEST_FLAG is ON"
else else
echo &quot;MY_TEST_FLAG is OFF&quot; echo "MY_TEST_FLAG is OFF"
fi fi
``` ```

View File

@ -83,8 +83,8 @@ positional) parameter may be validly referenced using a subscript,
because in most contexts, referring to the zeroth element of an array is because in most contexts, referring to the zeroth element of an array is
synonymous with referring to the array name without a subscript. synonymous with referring to the array name without a subscript.
# &quot;x&quot; is an ordinary non-array parameter. # "x" is an ordinary non-array parameter.
$ x=hi; printf '%s ' &quot;$x&quot; &quot;${x[0]}&quot;; echo &quot;${_[0]}&quot; $ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}"
hi hi hi hi hi hi
The only exceptions to this rule are in a few cases where the array The only exceptions to this rule are in a few cases where the array
@ -270,7 +270,7 @@ Yes, 12. Fine. You can take this number to walk through the array. Just
((n_elements=${#sentence[@]}, max_index=n_elements - 1)) ((n_elements=${#sentence[@]}, max_index=n_elements - 1))
for ((i = 0; i <= max_index; i++)); do for ((i = 0; i <= max_index; i++)); do
echo &quot;Element $i: '${sentence[i]}'&quot; echo "Element $i: '${sentence[i]}'"
done done
You always have to remember that, it seems newbies have problems You always have to remember that, it seems newbies have problems
@ -291,7 +291,7 @@ think you could just do
$ echo ${#sentence[@]} $ echo ${#sentence[@]}
1 1
# omit calculating max_index as above, and iterate as one-liner # omit calculating max_index as above, and iterate as one-liner
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo &quot;Element $i: '${sentence[i]}'&quot; ; done $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
Element 0: 'NAMES' Element 0: 'NAMES'
Obviously that's wrong. What about Obviously that's wrong. What about
@ -302,16 +302,16 @@ Obviously that's wrong. What about
$ echo ${#sentence[*]} $ echo ${#sentence[*]}
1 1
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo &quot;Element $i: '${sentence[i]}'&quot; ; done $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
Element 0: 'Peter' Element 0: 'Peter'
So what's the **right** way? The (slightly ugly) answer is, reuse the So what's the **right** way? The (slightly ugly) answer is, reuse the
enumeration syntax: enumeration syntax:
$ unset sentence ; declare -a sentence=(&quot;${NAMES[@]}&quot;) $ unset sentence ; declare -a sentence=("${NAMES[@]}")
$ echo ${#sentence[@]} $ echo ${#sentence[@]}
4 4
$ for ((i = 0; i < ${#sentence[@]}; i++)); do echo &quot;Element $i: '${sentence[i]}'&quot; ; done $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done
Element 0: 'Peter' Element 0: 'Peter'
Element 1: 'Anna' Element 1: 'Anna'
Element 2: 'Greg' Element 2: 'Greg'
@ -335,7 +335,7 @@ starting at zero) just is replaced with an arbitrary string:
in memory like they were declared, it could look like this: in memory like they were declared, it could look like this:
# output from 'set' command # output from 'set' command
sentence=([End]=&quot;in what you send&quot; [Middle]=&quot;you accept, and conservative &quot; [Begin]=&quot;Be liberal in what &quot; [&quot;Very end&quot;]=&quot;...&quot;) sentence=([End]="in what you send" [Middle]="you accept, and conservative " [Begin]="Be liberal in what " ["Very end"]="...")
This effectively means, you can get the data back with This effectively means, you can get the data back with
`"${sentence[@]}"`, of course (just like with numerical indexing), but `"${sentence[@]}"`, of course (just like with numerical indexing), but
@ -343,10 +343,10 @@ you can't rely on a specific order. If you want to store ordered data,
or re-order data, go with numerical indexes. For associative arrays, you or re-order data, go with numerical indexes. For associative arrays, you
usually query known index values: usually query known index values:
for element in Begin Middle End &quot;Very end&quot;; do for element in Begin Middle End "Very end"; do
printf &quot;%s&quot; &quot;${sentence[$element]}&quot; printf "%s" "${sentence[$element]}"
done done
printf &quot;\n&quot; printf "\n"
**A nice code example:** Checking for duplicate files using an **A nice code example:** Checking for duplicate files using an
associative array indexed with the SHA sum of the files: associative array indexed with the SHA sum of the files:
@ -356,9 +356,9 @@ associative array indexed with the SHA sum of the files:
unset flist; declare -A flist; unset flist; declare -A flist;
while read -r sum fname; do while read -r sum fname; do
if [[ ${flist[$sum]} ]]; then if [[ ${flist[$sum]} ]]; then
printf 'rm -- &quot;%s&quot; # Same as >%s<\n' &quot;$fname&quot; &quot;${flist[$sum]}&quot; printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}"
else else
flist[$sum]=&quot;$fname&quot; flist[$sum]="$fname"
fi fi
done < <(find . -type f -exec sha256sum {} +) >rmdups done < <(find . -type f -exec sha256sum {} +) >rmdups
@ -370,8 +370,8 @@ arrays, then values are considered as arithmetic for both compound and
ordinary assignment, and the += operator is modified in the same way as ordinary assignment, and the += operator is modified in the same way as
for ordinary integer variables. for ordinary integer variables.
~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]=&quot;a[2]&quot;)' 'a+=(42 [a[4]]+=3)'; declare -p a ) ~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]="a[2]")' 'a+=(42 [a[4]]+=3)'; declare -p a )
declare -ai a='([0]=&quot;6&quot; [2]=&quot;4&quot; [4]=&quot;7&quot; [5]=&quot;42&quot;)' declare -ai a='([0]="6" [2]="4" [4]="7" [5]="42")'
`a[0]` is assigned to the result of `2+4`. `a[2]` gets the result of `a[0]` is assigned to the result of `2+4`. `a[2]` gets the result of
`2+2`. The last index in the first assignment is the result of `a[2]`, `2+2`. The last index in the first assignment is the result of `a[2]`,
@ -428,29 +428,29 @@ without eval using the aforementioned special compound assignment
expansion. expansion.
isSubset() { isSubset() {
local -a 'xkeys=(&quot;${!'&quot;$1&quot;'[@]}&quot;)' 'ykeys=(&quot;${!'&quot;$2&quot;'[@]}&quot;)' local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'
set -- &quot;${@/%/[key]}&quot; set -- "${@/%/[key]}"
(( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1 (( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1
local key local key
for key in &quot;${xkeys[@]}&quot;; do for key in "${xkeys[@]}"; do
[[ ${!2+_} && ${!1} == ${!2} ]] || return 1 [[ ${!2+_} && ${!1} == ${!2} ]] || return 1
done done
} }
main() { main() {
# &quot;a&quot; is a subset of &quot;b&quot; # "a" is a subset of "b"
local -a 'a=({0..5})' 'b=({0..10})' local -a 'a=({0..5})' 'b=({0..10})'
isSubset a b isSubset a b
echo $? # true echo $? # true
# &quot;a&quot; contains a key not in &quot;b&quot; # "a" contains a key not in "b"
local -a 'a=([5]=5 {6..11})' 'b=({0..10})' local -a 'a=([5]=5 {6..11})' 'b=({0..10})'
isSubset a b isSubset a b
echo $? # false echo $? # false
# &quot;a&quot; contains an element whose value != the corresponding member of &quot;b&quot; # "a" contains an element whose value != the corresponding member of "b"
local -a 'a=([5]=5 6 8 9 10)' 'b=({0..10})' local -a 'a=([5]=5 6 8 9 10)' 'b=({0..10})'
isSubset a b isSubset a b
echo $? # false echo $? # false
@ -465,7 +465,7 @@ dynamically calls a function whose name is resolved from the array.
callFuncs() { callFuncs() {
# Set up indirect references as positional parameters to minimize local name collisions. # Set up indirect references as positional parameters to minimize local name collisions.
set -- &quot;${@:1:3}&quot; ${2+'a[&quot;$1&quot;]' &quot;$1&quot;'[&quot;$2&quot;]'} set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'}
# The only way to test for set but null parameters is unfortunately to test each individually. # The only way to test for set but null parameters is unfortunately to test each individually.
local x local x
@ -477,23 +477,23 @@ dynamically calls a function whose name is resolved from the array.
[foo]='([r]=f [s]=g [t]=h)' [foo]='([r]=f [s]=g [t]=h)'
[bar]='([u]=i [v]=j [w]=k)' [bar]='([u]=i [v]=j [w]=k)'
[baz]='([x]=l [y]=m [z]=n)' [baz]='([x]=l [y]=m [z]=n)'
) ${4+${a[&quot;$1&quot;]+&quot;${1}=${!3}&quot;}} # For example, if &quot;$1&quot; is &quot;bar&quot; then define a new array: bar=([u]=i [v]=j [w]=k) ) ${4+${a["$1"]+"${1}=${!3}"}} # For example, if "$1" is "bar" then define a new array: bar=([u]=i [v]=j [w]=k)
${4+${a[&quot;$1&quot;]+&quot;${!4-:}&quot;}} # Now just lookup the new array. for inputs: &quot;bar&quot; &quot;v&quot;, the function named &quot;j&quot; will be called, which prints &quot;j&quot; to stdout. ${4+${a["$1"]+"${!4-:}"}} # Now just lookup the new array. for inputs: "bar" "v", the function named "j" will be called, which prints "j" to stdout.
} }
main() { main() {
# Define functions named {f..n} which just print their own names. # Define functions named {f..n} which just print their own names.
local fun='() { echo &quot;$FUNCNAME&quot;; }' x local fun='() { echo "$FUNCNAME"; }' x
for x in {f..n}; do for x in {f..n}; do
eval &quot;${x}${fun}&quot; eval "${x}${fun}"
done done
callFuncs &quot;$@&quot; callFuncs "$@"
} }
main &quot;$@&quot; main "$@"
## Bugs and Portability Considerations ## Bugs and Portability Considerations
@ -537,7 +537,7 @@ dynamically calls a function whose name is resolved from the array.
` $ ksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=([3]=blah [0]=bork [1]=blarg [2]=zooj); typeset -p a; }; f' # ksh93 forces appending to the array, disregarding subscripts ` $ ksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=([3]=blah [0]=bork [1]=blarg [2]=zooj); typeset -p a; }; f' # ksh93 forces appending to the array, disregarding subscripts
typeset -a a=(foo bar baz '[3]=blah' '[0]=bork' '[1]=blarg' '[2]=zooj') typeset -a a=(foo bar baz '[3]=blah' '[0]=bork' '[1]=blarg' '[2]=zooj')
$ bash -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Bash applies += to every individual subscript. $ bash -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Bash applies += to every individual subscript.
declare -a a='([0]=&quot;foobork&quot; [1]=&quot;barblarg&quot; [2]=&quot;bazzooj&quot; [3]=&quot;blah&quot;)' declare -a a='([0]="foobork" [1]="barblarg" [2]="bazzooj" [3]="blah")'
$ mksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Mksh does like Bash, but clobbers previous values rather than appending. $ mksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Mksh does like Bash, but clobbers previous values rather than appending.
set -A a set -A a
typeset a[0]=bork typeset a[0]=bork
@ -570,7 +570,7 @@ dynamically calls a function whose name is resolved from the array.
typeset -a arr=(foo bar bork baz) typeset -a arr=(foo bar bork baz)
$ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting! $ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting!
typeset -a arr=(bar baz bork foo) typeset -a arr=(bar baz bork foo)
$ mksh -c 'set -sA arr -- foo &quot;[3]=bar&quot; &quot;[2]=baz&quot; &quot;[7]=bork&quot;; typeset -p arr' # Probably a bug. I think the maintainer is aware of it. $ mksh -c 'set -sA arr -- foo "[3]=bar" "[2]=baz" "[7]=bork"; typeset -p arr' # Probably a bug. I think the maintainer is aware of it.
set -A arr set -A arr
typeset arr[2]=baz typeset arr[2]=baz
typeset arr[3]=bar typeset arr[3]=bar
@ -610,11 +610,11 @@ dynamically calls a function whose name is resolved from the array.
the right of the `=` sign. This is fixed in 4.3, so that each the right of the `=` sign. This is fixed in 4.3, so that each
subscript assignment statement is expanded following the same rules as subscript assignment statement is expanded following the same rules as
an ordinary assignment. This also works correctly in ksh93. an ordinary assignment. This also works correctly in ksh93.
`$ touch '[1]=a'; bash -c 'a=([1]=*); echo &quot;${a[@]}&quot;' `$ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"'
[1]=a [1]=a
` mksh has a similar but even worse problem in that the entire ` mksh has a similar but even worse problem in that the entire
subscript is considered a glob. subscript is considered a glob.
`$ touch 1=a; mksh -c 'a=([123]=*); print -r -- &quot;${a[@]}&quot;' `$ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"'
1=a 1=a
` `
- **Fixed in 4.3** In addition to the above globbing issue, assignments - **Fixed in 4.3** In addition to the above globbing issue, assignments
@ -635,13 +635,13 @@ dynamically calls a function whose name is resolved from the array.
`let`, `eval`, other declaration commands, and maybe more. `let`, `eval`, other declaration commands, and maybe more.
- **Fixed in 4.3** Indirection combined with another modifier expands - **Fixed in 4.3** Indirection combined with another modifier expands
arrays to a single word. arrays to a single word.
`$ a=({a..c}) b=a[@]; printf '<%s> ' &quot;${!b}&quot;; echo; printf '<%s> ' &quot;${!b/%/foo}&quot;; echo `$ a=({a..c}) b=a[@]; printf '<%s> ' "${!b}"; echo; printf '<%s> ' "${!b/%/foo}"; echo
<a> <b> <c> <a> <b> <c>
<a b cfoo> <a b cfoo>
` `
- **Fixed in 4.3** Process substitutions are evaluated within array - **Fixed in 4.3** Process substitutions are evaluated within array
indexes. Zsh and ksh don't do this in any arithmetic context. indexes. Zsh and ksh don't do this in any arithmetic context.
`# print &quot;moo&quot; `# print "moo"
dev=fd=1 _[1<(echo moo >&2)]= dev=fd=1 _[1<(echo moo >&2)]=
# Fork bomb # Fork bomb

View File

@ -71,7 +71,7 @@ and vice versa. In this example, the commands in the if stanza will be
executed if the pattern "^root:" is **not** found in `/etc/passwd`: executed if the pattern "^root:" is **not** found in `/etc/passwd`:
if ! grep '^root:' /etc/passwd; then if ! grep '^root:' /etc/passwd; then
echo &quot;No root user defined... eh?&quot; echo "No root user defined... eh?"
fi fi
Yes, this is also a pipeline (although there is no pipe!), because the Yes, this is also a pipeline (although there is no pipe!), because the
@ -179,7 +179,7 @@ The definition is easy (one of many possibilities):
which is usually used with the `{...; }` compound command, and thus which is usually used with the `{...; }` compound command, and thus
looks like: looks like:
print_help() { echo &quot;Sorry, no help available&quot;; } print_help() { echo "Sorry, no help available"; }
As above, a function definition can have any [compound As above, a function definition can have any [compound
command](basicgrammar#compound_commands) as a body. Structures like command](basicgrammar#compound_commands) as a body. Structures like
@ -223,11 +223,11 @@ like this:
mycmd() mycmd()
{ {
# this $1 belongs to the function! # this $1 belongs to the function!
find / -iname &quot;$1&quot; find / -iname "$1"
} }
# this $1 belongs the script itself! # this $1 belongs the script itself!
mycmd &quot;$1&quot; # Execute command immediately after defining function mycmd "$1" # Execute command immediately after defining function
exit 0 exit 0
@ -239,7 +239,7 @@ variables. Variables with the content "*() ....*".
Something similar to the following works without "officially" declaring Something similar to the following works without "officially" declaring
a function: a function:
$ export testfn=&quot;() { echo test; }&quot; $ export testfn="() { echo test; }"
$ bash -c testfn $ bash -c testfn
test test
$ $
@ -284,7 +284,7 @@ FIXME more...
<u>A (very) simple command</u> <u>A (very) simple command</u>
echo &quot;Hello world...&quot; echo "Hello world..."
<u>All of the following are simple commands</u> <u>All of the following are simple commands</u>
@ -292,7 +292,7 @@ FIXME more...
>tmpfile >tmpfile
{x}<&quot;$x&quot; _=${x=<(echo moo)} <&0$(cat <&&quot;$x&quot; >&2) {x}<"$x" _=${x=<(echo moo)} <&0$(cat <&"$x" >&2)
------------------------------------------------------------------------ ------------------------------------------------------------------------

View File

@ -114,7 +114,7 @@ current value.
<!-- --> <!-- -->
for ((x = 0 ; x <= 100 ; x++)); do for ((x = 0 ; x <= 100 ; x++)); do
echo &quot;Counter: $x&quot; echo "Counter: $x"
done done
### Stepping counter ### Stepping counter
@ -124,7 +124,7 @@ above), but the **change** that is made is a `x += 10`. That means, it
will count from 0 to 100, but with a **step of 10**. will count from 0 to 100, but with a **step of 10**.
for ((x = 0 ; x <= 100 ; x += 10)); do for ((x = 0 ; x <= 100 ; x += 10)); do
echo &quot;Counter: $x&quot; echo "Counter: $x"
done done
### Bits analyzer ### Bits analyzer
@ -148,15 +148,15 @@ bits).
function main { function main {
[[ $1 == +([0-9]) ]] || return [[ $1 == +([0-9]) ]] || return
typeset result typeset result
if (( $(ksh -c 'printf %..2d $1' _ &quot;$1&quot;) == ( result = $(toBin &quot;$1&quot;) ) )); then if (( $(ksh -c 'printf %..2d $1' _ "$1") == ( result = $(toBin "$1") ) )); then
printf '%s is %s in base 2!\n' &quot;$1&quot; &quot;$result&quot; printf '%s is %s in base 2!\n' "$1" "$result"
else else
echo 'Oops, something went wrong with our calculation.' >&2 echo 'Oops, something went wrong with our calculation.' >&2
exit 1 exit 1
fi fi
} }
main &quot;${1:-123}&quot; main "${1:-123}"
# vim: set fenc=utf-8 ff=unix ft=sh : # vim: set fenc=utf-8 ff=unix ft=sh :
@ -191,7 +191,7 @@ demonstrating more complicated arithmetic expressions with multiple
variables. variables.
for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);)); do for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);)); do
printf '%*s\n' &quot;$((n+1))&quot; &quot;$n&quot; printf '%*s\n' "$((n+1))" "$n"
done done
\<code\> ~ \$ bash \<(xclip -o) 1 \<code\> ~ \$ bash \<(xclip -o) 1

View File

@ -36,7 +36,7 @@ expansion*; *arithmetic*, *command* and *process substitution*; and
*quote removal*. **No word splitting, brace, or pathname expansion is *quote removal*. **No word splitting, brace, or pathname expansion is
done**, which means you can leave expansions unquoted without problems: done**, which means you can leave expansions unquoted without problems:
var=&quot;test word&quot; var="test word"
case $var in case $var in
... ...
@ -78,7 +78,7 @@ Another one of my stupid examples...
exit 1 exit 1
;; ;;
*) *)
echo &quot;Unknown fruit - sure it isn't toxic?&quot; echo "Unknown fruit - sure it isn't toxic?"
esac esac
Here's a practical example showing a common pattern involving a `case` Here's a practical example showing a common pattern involving a `case`
@ -95,13 +95,13 @@ function clk {
case $1 in case $1 in
low|high|default) low|high|default)
printf '%s\n' &quot;temp: $(<${base}/hwmon/hwmon0/temp1_input)C&quot; &quot;old profile: $(<${base}/power_profile)&quot; printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "old profile: $(<${base}/power_profile)"
echo &quot;$1&quot; >${base}/power_profile echo "$1" >${base}/power_profile
echo &quot;new profile: $(<${base}/power_profile)&quot; echo "new profile: $(<${base}/power_profile)"
;; ;;
*) *)
echo &quot;Usage: $FUNCNAME [ low | high | default ]&quot; echo "Usage: $FUNCNAME [ low | high | default ]"
printf '%s\n' &quot;temp: $(<${base}/hwmon/hwmon0/temp1_input)C&quot; &quot;current profile: $(<${base}/power_profile)&quot; printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "current profile: $(<${base}/power_profile)"
esac esac
} }
``` ```
@ -113,25 +113,25 @@ between blocks using `;&`, and the non-short-circuiting `;;&` operator:
#!/usr/bin/env bash #!/usr/bin/env bash
f() { f() {
local -a &quot;$@&quot; local -a "$@"
local x local x
for x; do for x; do
case $x in case $x in
$1) $1)
local &quot;$x&quot;'+=(1)' ;;& local "$x"'+=(1)' ;;&
$2) $2)
local &quot;$x&quot;'+=(2)' ;& local "$x"'+=(2)' ;&
$3) $3)
local &quot;$x&quot;'+=(3)' ;; local "$x"'+=(3)' ;;
$1|$2) $1|$2)
local &quot;$x&quot;'+=(4)' local "$x"'+=(4)'
esac esac
IFS=, local -a &quot;$x&quot;'=(&quot;${x}: ${'&quot;$x&quot;'[*]}&quot;)' IFS=, local -a "$x"'=("${x}: ${'"$x"'[*]}")'
done done
for x; do for x; do
echo &quot;${!x}&quot; echo "${!x}"
done done
} }

View File

@ -71,8 +71,8 @@ With some array syntax (see [arrays](/syntax/arrays)) you can easily
mass-expanding all elements): mass-expanding all elements):
``` bash ``` bash
for element in &quot;${myarray[@]}&quot;; do for element in "${myarray[@]}"; do
echo &quot;Element: $element&quot; echo "Element: $element"
done done
``` ```
@ -80,8 +80,8 @@ Another way is to mass-expand all used indexes and access the array by
index: index:
``` bash ``` bash
for index in &quot;${!myarray[@]}&quot;; do for index in "${!myarray[@]}"; do
echo &quot;Element[$index]: ${myarray[$index]}&quot; echo "Element[$index]: ${myarray[$index]}"
done done
``` ```
@ -95,7 +95,7 @@ arguments to a command will be interpreted and parsed, and finally used:
argtest() { argtest() {
n=1 n=1
for arg; do for arg; do
echo &quot;Argument $((n++)): \&quot;$arg\&quot;&quot; echo "Argument $((n++)): \"$arg\""
done done
} }
``` ```
@ -108,16 +108,16 @@ filenames in a directory:
``` bash ``` bash
for fn in *; do for fn in *; do
if [ -h &quot;$fn&quot; ]; then if [ -h "$fn" ]; then
echo -n &quot;Symlink: &quot; echo -n "Symlink: "
elif [ -d &quot;$fn&quot; ]; then elif [ -d "$fn" ]; then
echo -n &quot;Dir: &quot; echo -n "Dir: "
elif [ -f &quot;$fn&quot; ]; then elif [ -f "$fn" ]; then
echo -n &quot;File: &quot; echo -n "File: "
else else
echo -n &quot;Unknown: &quot; echo -n "Unknown: "
fi fi
echo &quot;$fn&quot; echo "$fn"
done done
``` ```

View File

@ -22,10 +22,10 @@ status (exit code)](/scripting/basics#exit_codes) of the list.
The input and output **filedescriptors** are cumulative: The input and output **filedescriptors** are cumulative:
{ {
echo &quot;PASSWD follows&quot; echo "PASSWD follows"
cat /etc/passwd cat /etc/passwd
echo echo
echo &quot;GROUPS follows&quot; echo "GROUPS follows"
cat /etc/group cat /etc/group
} >output.txt } >output.txt
@ -34,10 +34,10 @@ definition](/syntax/basicgrammar#shell_function_definitions), though not
the only compound command that's valid there: the only compound command that's valid there:
print_help() { print_help() {
echo &quot;Options:&quot; echo "Options:"
echo &quot;-h This help text&quot; echo "-h This help text"
echo &quot;-f FILE Use config file FILE&quot; echo "-f FILE Use config file FILE"
echo &quot;-u USER Run as user USER&quot; echo "-u USER Run as user USER"
} }
## Examples ## Examples
@ -46,10 +46,10 @@ the only compound command that's valid there:
try_catch() { try_catch() {
{ # Try-block: { # Try-block:
eval &quot;$@&quot; eval "$@"
} || } ||
{ # Catch-block: { # Catch-block:
echo &quot;An error occurred&quot; echo "An error occurred"
return -1 return -1
} }
} }

View File

@ -15,9 +15,9 @@ etc...) are reflected in the "main shell".
Execute a command in a different directory. Execute a command in a different directory.
``` bash ``` bash
echo &quot;$PWD&quot; echo "$PWD"
( cd /usr; echo &quot;$PWD&quot; ) ( cd /usr; echo "$PWD" )
echo &quot;$PWD&quot; # Still in the original directory. echo "$PWD" # Still in the original directory.
``` ```
## Portability considerations ## Portability considerations

View File

@ -51,15 +51,15 @@ commands of the condition that succeeded.
**Check if a specific user exists in /etc/passwd :-)** **Check if a specific user exists in /etc/passwd :-)**
if grep ^myuser: /etc/passwd >/dev/null 2>&1; then if grep ^myuser: /etc/passwd >/dev/null 2>&1; then
echo &quot;Yes, it seems I'm real&quot; echo "Yes, it seems I'm real"
else else
echo &quot;Uh - am I a ghost?&quot; echo "Uh - am I a ghost?"
fi fi
**Mount with check** **Mount with check**
if ! mount /mnt/backup >/dev/null 2>&1; then if ! mount /mnt/backup >/dev/null 2>&1; then
echo &quot;FATAL: backup mount failed&quot; >&2 echo "FATAL: backup mount failed" >&2
exit 1 exit 1
fi fi
@ -67,7 +67,7 @@ commands of the condition that succeeded.
It's perfectly valid to do: It's perfectly valid to do:
if echo &quot;I'm testing!&quot;; [ -e /some/file ]; then if echo "I'm testing!"; [ -e /some/file ]; then
... ...
fi fi
@ -80,8 +80,8 @@ the very last command executed in the condition-list (here: The
A complete pipe can also be used as condition. It's very similar to the A complete pipe can also be used as condition. It's very similar to the
example above (multiple commands): example above (multiple commands):
if echo &quot;Hello world!&quot; | grep -i hello >/dev/null 2>&1; then if echo "Hello world!" | grep -i hello >/dev/null 2>&1; then
echo &quot;You just said 'hello', yeah?&quot; echo "You just said 'hello', yeah?"
fi fi
## Portability considerations ## Portability considerations

View File

@ -18,8 +18,8 @@ function printSum {
typeset -A args typeset -A args
typeset name typeset name
for name in first second; do for name in first second; do
[[ -t 0 ]] && printf 'Enter %s positive integer: ' &quot;$name&quot; >&2 [[ -t 0 ]] && printf 'Enter %s positive integer: ' "$name" >&2
read -r ${BASH_VERSION+-e} &quot;args[$name]&quot; read -r ${BASH_VERSION+-e} "args[$name]"
[[ ${args[$name]} == +([[:digit:]]) ]] || return 1 # Validation is extremely important whenever user input is used in arithmetic. [[ ${args[$name]} == +([[:digit:]]) ]] || return 1 # Validation is extremely important whenever user input is used in arithmetic.
done done
printf 'The sum is %d.' $((${args[first]} + ${args[second]})) printf 'The sum is %d.' $((${args[first]} + ${args[second]}))
@ -49,7 +49,7 @@ x=1
echo $((x)) # Good. echo $((x)) # Good.
echo $(($x)) # Ok. Avoid expansions within arithmetic. Use variables directly. echo $(($x)) # Ok. Avoid expansions within arithmetic. Use variables directly.
echo $((&quot;$x&quot;)) # Error. There is no quote-removal in arithmetic contexts. It expands to $((&quot;1&quot;)), which is an invalid arithmetic expression. echo $(("$x")) # Error. There is no quote-removal in arithmetic contexts. It expands to $(("1")), which is an invalid arithmetic expression.
echo $((x[0])) # Good. echo $((x[0])) # Good.
echo $((${x[0]})) # Ok. Nested expansion again. echo $((${x[0]})) # Ok. Nested expansion again.
echo $((${x[$((${x[!$x]}-$x))]})) # Same as above but more ridiculous. echo $((${x[$((${x[!$x]}-$x))]})) # Same as above but more ridiculous.

View File

@ -41,7 +41,7 @@ requires that the entire command be properly escaped to avoid unexpected
expansions. If the sequence expansion is to be assigned to an array, expansions. If the sequence expansion is to be assigned to an array,
another method is possible using [declaration another method is possible using [declaration
commands](/commands/builtin/declare): commands](/commands/builtin/declare):
`declare -a 'pics=(img{'&quot;$a..$b&quot;'}.png)'; mv &quot;${pics[@]}&quot; ../imgs` `declare -a 'pics=(img{'"$a..$b"'}.png)'; mv "${pics[@]}" ../imgs`
This is significantly safer, but one must still be careful to control This is significantly safer, but one must still be careful to control
the values of \$a and \$b. Both the exact quoting, and explicitly the values of \$a and \$b. Both the exact quoting, and explicitly
including "-a" are important. including "-a" are important.
@ -181,18 +181,18 @@ for.
<!-- --> <!-- -->
for i in 0{1..9} 10; do printf &quot;%s\n&quot; &quot;$i&quot;;done for i in 0{1..9} 10; do printf "%s\n" "$i";done
If you need to create words with the number embedded, you can use nested If you need to create words with the number embedded, you can use nested
brace: brace:
printf &quot;%s\n&quot; img{00{1..9},0{10..99},{100..999}}.png printf "%s\n" img{00{1..9},0{10..99},{100..999}}.png
- Formatting the numbers with printf: - Formatting the numbers with printf:
<!-- --> <!-- -->
echo $(printf &quot;img%02d.png &quot; {1..99}) echo $(printf "img%02d.png " {1..99})
See the [text below](#news_in_bash_4.0) for a new Bash 4 method. See the [text below](#news_in_bash_4.0) for a new Bash 4 method.
@ -218,18 +218,18 @@ any number of arguments.
function braceify { function braceify {
[[ $1 == +([[:digit:]]) ]] || return [[ $1 == +([[:digit:]]) ]] || return
typeset -a a typeset -a a
read -ra a < <(factor &quot;$1&quot;) read -ra a < <(factor "$1")
eval &quot;echo $(printf '{$(printf ,%%.s {1..%s})}' &quot;${a[@]:1}&quot;)&quot; eval "echo $(printf '{$(printf ,%%.s {1..%s})}' "${a[@]:1}")"
} }
printf 'eval printf &quot;$arg&quot;%s' &quot;$(braceify 1000000)&quot; printf 'eval printf "$arg"%s' "$(braceify 1000000)"
"Braceify" generates the expansion code itself. In this example we "Braceify" generates the expansion code itself. In this example we
inject that output into a template which displays the most terse brace inject that output into a template which displays the most terse brace
expansion code that would expand `"$arg"` 1,000,000 times if evaluated. expansion code that would expand `"$arg"` 1,000,000 times if evaluated.
In this case, the output is: In this case, the output is:
eval printf &quot;$arg&quot;{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,} eval printf "$arg"{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}
\</div\> \</div\>

View File

@ -73,7 +73,7 @@ substitution, while every `$()`-construct opens an own, subsequent
parsing step. Everything inside `$()` is interpreted as if written parsing step. Everything inside `$()` is interpreted as if written
normal on a commandline. No special escaping of **nothing** is needed: normal on a commandline. No special escaping of **nothing** is needed:
echo &quot;$(echo &quot;$(ls)&quot;)&quot; # nested double-quotes - no problem echo "$(echo "$(ls)")" # nested double-quotes - no problem
**<u>Constructs you should avoid</u>** **<u>Constructs you should avoid</u>**
@ -91,10 +91,10 @@ substitution step and echo prints "ls" and ")":
It seems that every closing ")" confuses this construct. Also a (very It seems that every closing ")" confuses this construct. Also a (very
uncommon ;-)) construct like: uncommon ;-)) construct like:
echo $(read VAR; case &quot;$var&quot; in foo) blah ;; esac) # spits out some error, when it sees the &quot;;;&quot; echo $(read VAR; case "$var" in foo) blah ;; esac) # spits out some error, when it sees the ";;"
# fixes it: # fixes it:
echo $(read VAR; case &quot;$var&quot; in (foo) blah ;; esac) # will work, but just let it be, please ;-) echo $(read VAR; case "$var" in (foo) blah ;; esac) # will work, but just let it be, please ;-)
**<u>Conclusion:</u>** **<u>Conclusion:</u>**
@ -110,19 +110,19 @@ In general, the `$()` should be the preferred method:
**To get the date:** **To get the date:**
DATE=&quot;$(date)&quot; DATE="$(date)"
**To copy a file and get `cp` error output:** **To copy a file and get `cp` error output:**
COPY_OUTPUT=&quot;$(cp file.txt /some/where 2>&1)&quot; COPY_OUTPUT="$(cp file.txt /some/where 2>&1)"
Attention: Here, you need to redirect `cp` `STDERR` to its `STDOUT` Attention: Here, you need to redirect `cp` `STDERR` to its `STDOUT`
target, because command substitution only catches `STDOUT`! target, because command substitution only catches `STDOUT`!
**Catch stdout and preserve trailing newlines:** **Catch stdout and preserve trailing newlines:**
var=$(echo -n $'\n'); echo -n &quot;$var&quot;; # $var == &quot;&quot; var=$(echo -n $'\n'); echo -n "$var"; # $var == ""
var=$(echo -n $'\n'; echo -n x); var=&quot;${var%x}&quot;; echo -n &quot;$var&quot; # $var == &quot;\n&quot; var=$(echo -n $'\n'; echo -n x); var="${var%x}"; echo -n "$var" # $var == "\n"
This adds "x" to the output, which prevents the trailing newlines of the This adds "x" to the output, which prevents the trailing newlines of the
previous commands' output from being deleted by \$(). previous commands' output from being deleted by \$().

View File

@ -8,7 +8,7 @@ A called program will never see the glob itself; it will only see the
expanded filenames as its arguments (here, all filenames matching expanded filenames as its arguments (here, all filenames matching
`*.log`): `*.log`):
grep &quot;changes:&quot; *.log grep "changes:" *.log
The base syntax for the pathname expansion is the [pattern The base syntax for the pathname expansion is the [pattern
matching](/syntax/pattern) syntax. The pattern you describe is matched matching](/syntax/pattern) syntax. The pattern you describe is matched
@ -59,7 +59,7 @@ Normally, when no glob specified matches an existing filename, no
pathname expansion is performed, and the globs are <u>**not**</u> pathname expansion is performed, and the globs are <u>**not**</u>
removed: removed:
$ echo &quot;Textfiles here:&quot; *.txt $ echo "Textfiles here:" *.txt
Textfiles here: *.txt Textfiles here: *.txt
In this example, no files matched the pattern, so the glob was left In this example, no files matched the pattern, so the glob was left
@ -69,9 +69,9 @@ This can be very annoying, for example when you drive a
[for-loop](/syntax/ccmd/classic_for) using the pathname expansion: [for-loop](/syntax/ccmd/classic_for) using the pathname expansion:
for filename in *.txt; do for filename in *.txt; do
echo &quot;=== BEGIN: $filename ===&quot; echo "=== BEGIN: $filename ==="
cat &quot;$filename&quot; cat "$filename"
echo &quot;=== END: $filename ===&quot; echo "=== END: $filename ==="
done done
When no file name matches the glob, the loop will not only output stupid When no file name matches the glob, the loop will not only output stupid
@ -85,7 +85,7 @@ even one iteration will be done. It just won't run.
So in our first example: So in our first example:
$ shopt -s nullglob $ shopt -s nullglob
$ echo &quot;Textfiles here:&quot; *.txt $ echo "Textfiles here:" *.txt
Textfiles here: Textfiles here:
and the glob is gone. and the glob is gone.

View File

@ -11,8 +11,8 @@ a changed command line: the elements are said to be **expanded** to or
The most simple example of this behaviour is a referenced variable: The most simple example of this behaviour is a referenced variable:
mystring=&quot;Hello world&quot; mystring="Hello world"
echo &quot;$mystring&quot; echo "$mystring"
The `echo` program definitely doesn't care about what a shell variable The `echo` program definitely doesn't care about what a shell variable
is. It is Bash's job to deal with the variable. Bash **expands** the is. It is Bash's job to deal with the variable. Bash **expands** the

View File

@ -71,7 +71,7 @@ file `/dev/fd/63`.
Consider the following: Consider the following:
``` bash ``` bash
diff <(ls &quot;$first_directory&quot;) <(ls &quot;$second_directory&quot;) diff <(ls "$first_directory") <(ls "$second_directory")
``` ```
This will compare the contents of each directory. In this command, each This will compare the contents of each directory. In this command, each
@ -102,7 +102,7 @@ find /etc -print0 | while IFS= read -rd '' _; do
((counter++)) ((counter++))
done done
echo &quot;$counter files&quot; # prints &quot;0 files&quot; echo "$counter files" # prints "0 files"
``` ```
Due to the pipe, the `while read; do ... done` part is executed in a Due to the pipe, the `while read; do ... done` part is executed in a
@ -121,7 +121,7 @@ while IFS= read -rN1 _; do
((counter++)) ((counter++))
done < <(find /etc -printf ' ') done < <(find /etc -printf ' ')
echo &quot;$counter files&quot; echo "$counter files"
``` ```
This is the normal input file redirection `< FILE`, just that the `FILE` This is the normal input file redirection `< FILE`, just that the `FILE`
@ -143,7 +143,7 @@ resemble "passable" objects. This results in converting the output of
``` bash ``` bash
f() { f() {
cat &quot;$1&quot; >&quot;$x&quot; cat "$1" >"$x"
} }
x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there') x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')
@ -167,7 +167,7 @@ See the above section on [\#scope](#scope)
other arithmetic contexts. Ksh and Zsh do not. (Possible Bug) other arithmetic contexts. Ksh and Zsh do not. (Possible Bug)
``` bash ``` bash
# print &quot;moo&quot; # print "moo"
dev=fd=1 _[1<(echo moo >&2)]= dev=fd=1 _[1<(echo moo >&2)]=
# fork bomb # fork bomb
${dev[${dev='dev[1>(${dev[dev]})]'}]} ${dev[${dev='dev[1>(${dev[dev]})]'}]}

View File

@ -49,7 +49,7 @@ This way you can correctly use the tilde expansion in your
**Spaces in the referenced pathes?** A construct like... **Spaces in the referenced pathes?** A construct like...
~/&quot;my directory&quot; ~/"my directory"
...is perfectly valid and works! ...is perfectly valid and works!
@ -83,11 +83,11 @@ at least regarding tilde expansion.
This expands to the value of the [PWD](/syntax/shellvars#PWD) variable, This expands to the value of the [PWD](/syntax/shellvars#PWD) variable,
which holds the currect working directory: which holds the currect working directory:
echo &quot;CWD is $PWD&quot; echo "CWD is $PWD"
is equivalent to (note it **must** be a separate word!): is equivalent to (note it **must** be a separate word!):
echo &quot;CWD is&quot; ~+ echo "CWD is" ~+
## Previous working directory ## Previous working directory

View File

@ -93,7 +93,7 @@ The behavior regarding the variable assignment errors can be tested:
echo PRE echo PRE
# The following is an assignment error! # The following is an assignment error!
# The &quot;echo TEST&quot; won't be executed, since the { ...; } is terminated # The "echo TEST" won't be executed, since the { ...; } is terminated
{ foo=$((8#9)); echo TEST; } { foo=$((8#9)); echo TEST; }
echo POST echo POST

View File

@ -35,13 +35,13 @@ Some examples:
# redirecting stderr in the pipe # redirecting stderr in the pipe
$ coproc { ls thisfiledoesntexist; read; } 2>&1 $ coproc { ls thisfiledoesntexist; read; } 2>&1
[2] 23084 [2] 23084
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' &quot;$x&quot; $ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
ls: cannot access thisfiledoesntexist: No such file or directory ls: cannot access thisfiledoesntexist: No such file or directory
``` ```
``` bash ``` bash
#let the output of the coprocess go to stdout #let the output of the coprocess go to stdout
$ { coproc mycoproc { awk '{print &quot;foo&quot; $0;fflush()}'; } >&3; } 3>&1 $ { coproc mycoproc { awk '{print "foo" $0;fflush()}'; } >&3; } 3>&1
[2] 23092 [2] 23092
$ echo bar >&${mycoproc[1]} $ echo bar >&${mycoproc[1]}
$ foobar $ foobar
@ -64,7 +64,7 @@ In Ksh you would do:
``` bash ``` bash
# ksh93 or mksh/pdksh derivatives # ksh93 or mksh/pdksh derivatives
ls |& # start a coprocess ls |& # start a coprocess
while IFS= read -rp file; do print -r -- &quot;$file&quot;; done # read its output while IFS= read -rp file; do print -r -- "$file"; done # read its output
``` ```
In bash: In bash:
@ -73,7 +73,7 @@ In bash:
#DOESN'T WORK #DOESN'T WORK
$ coproc ls $ coproc ls
[1] 23232 [1] 23232
$ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' &quot;$line&quot;; done $ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' "$line"; done
bash: read: line: invalid file descriptor specification bash: read: line: invalid file descriptor specification
[1]+ Done coproc COPROC ls [1]+ Done coproc COPROC ls
``` ```
@ -95,7 +95,7 @@ with `sed`:
$ coproc sed s/^/foo/ $ coproc sed s/^/foo/
[1] 22981 [1] 22981
$ echo bar >&${COPROC[1]} $ echo bar >&${COPROC[1]}
$ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo &quot;nothing read&quot; $ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo "nothing read"
nothing read nothing read
``` ```
@ -116,9 +116,9 @@ continuously read the output of a coprocess and `echo` the result:
``` bash ``` bash
#NOT WORKING #NOT WORKING
$ coproc awk '{print &quot;foo&quot; $0;fflush()}' $ coproc awk '{print "foo" $0;fflush()}'
[2] 23100 [2] 23100
$ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' &quot;$x&quot;; done & $ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' "$x"; done &
[3] 23104 [3] 23104
bash: line 243: read: 61: invalid file descriptor: Bad file descriptor bash: line 243: read: 61: invalid file descriptor: Bad file descriptor
``` ```
@ -132,10 +132,10 @@ A possible workaround:
#WARNING: for illustration purpose ONLY #WARNING: for illustration purpose ONLY
# this is not the way to make the coprocess print its output # this is not the way to make the coprocess print its output
# to stdout, see the redirections above. # to stdout, see the redirections above.
$ coproc awk '{print &quot;foo&quot; $0;fflush()}' $ coproc awk '{print "foo" $0;fflush()}'
[2] 23109 [2] 23109
$ exec 3<&${COPROC[0]} $ exec 3<&${COPROC[0]}
$ while IFS= read -ru 3 x; do printf '%s\n' &quot;$x&quot;; done & $ while IFS= read -ru 3 x; do printf '%s\n' "$x"; done &
[3] 23110 [3] 23110
$ echo bar >&${COPROC[1]} $ echo bar >&${COPROC[1]}
$ foobar $ foobar
@ -152,7 +152,7 @@ assigns FDs to a default array named `COPROC` if no `NAME` is supplied.
Here's an example: Here's an example:
``` bash ``` bash
$ coproc awk '{print &quot;foo&quot; $0;fflush()}' $ coproc awk '{print "foo" $0;fflush()}'
[1] 22978 [1] 22978
``` ```
@ -167,7 +167,7 @@ $ echo bar >&${COPROC[1]}
And then read its output: And then read its output:
``` bash ``` bash
$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' &quot;$x&quot; $ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
foobar foobar
``` ```
@ -175,7 +175,7 @@ When we don't need our command anymore, we can kill it via its pid:
$ kill $COPROC_PID $ kill $COPROC_PID
$ $
[1]+ Terminated coproc COPROC awk '{print &quot;foo&quot; $0;fflush()}' [1]+ Terminated coproc COPROC awk '{print "foo" $0;fflush()}'
### Named Coprocess ### Named Coprocess
@ -184,14 +184,14 @@ when defining a function), and the resulting FDs will be assigned to the
indexed array `NAME` we supply instead. indexed array `NAME` we supply instead.
``` bash ``` bash
$ coproc mycoproc { awk '{print &quot;foo&quot; $0;fflush()}' ;} $ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;}
[1] 23058 [1] 23058
$ echo bar >&${mycoproc[1]} $ echo bar >&${mycoproc[1]}
$ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' &quot;$x&quot; $ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' "$x"
foobar foobar
$ kill $mycoproc_PID $ kill $mycoproc_PID
$ $
[1]+ Terminated coproc mycoproc { awk '{print &quot;foo&quot; $0;fflush()}'; } [1]+ Terminated coproc mycoproc { awk '{print "foo" $0;fflush()}'; }
``` ```
### Redirecting the output of a script to a file and to the screen ### Redirecting the output of a script to a file and to the screen

View File

@ -91,8 +91,8 @@ be interpreted as part of the parameter name otherwise. Compare these
two expressions (`WORD="car"` for example), where we want to print a two expressions (`WORD="car"` for example), where we want to print a
word with a trailing "s": word with a trailing "s":
echo &quot;The plural of $WORD is most likely $WORDs&quot; echo "The plural of $WORD is most likely $WORDs"
echo &quot;The plural of $WORD is most likely ${WORD}s&quot; echo "The plural of $WORD is most likely ${WORD}s"
<u>Why does the first one fail?</u> It prints nothing, because a <u>Why does the first one fail?</u> It prints nothing, because a
parameter (variable) named "`WORDs`" is undefined and thus printed as "" parameter (variable) named "`WORDs`" is undefined and thus printed as ""
@ -108,8 +108,8 @@ everything in UNIX(r)) **case sensitive!**
The second form with the curly braces is also needed to access The second form with the curly braces is also needed to access
positional parameters (arguments to a script) beyond `$9`: positional parameters (arguments to a script) beyond `$9`:
echo &quot;Argument 1 is: $1&quot; echo "Argument 1 is: $1"
echo &quot;Argument 10 is: ${10}&quot; echo "Argument 10 is: ${10}"
### Simple usage: Arrays ### Simple usage: Arrays
@ -146,23 +146,23 @@ then `${!PARAMETER}` will expand to the value of the parameter named
read -rep 'Which variable do you want to inspect? ' look_var read -rep 'Which variable do you want to inspect? ' look_var
printf 'The value of &quot;%s&quot; is: &quot;%s&quot;\n' &quot;$look_var&quot; &quot;${!look_var}&quot; printf 'The value of "%s" is: "%s"\n' "$look_var" "${!look_var}"
Of course the indirection also works with special variables: Of course the indirection also works with special variables:
# set some fake positional parameters # set some fake positional parameters
set one two three four set one two three four
# get the LAST argument (&quot;#&quot; stores the number of arguments, so &quot;!#&quot; will reference the LAST argument) # get the LAST argument ("#" stores the number of arguments, so "!#" will reference the LAST argument)
echo ${!#} echo ${!#}
You can think of this mechanism as being roughly equivalent to taking You can think of this mechanism as being roughly equivalent to taking
any parameter expansion that begins with the parameter name, and any parameter expansion that begins with the parameter name, and
substituting the `!PARAMETER` part with the value of PARAMETER. substituting the `!PARAMETER` part with the value of PARAMETER.
echo &quot;${!var^^}&quot; echo "${!var^^}"
# ...is equivalent to # ...is equivalent to
eval 'echo &quot;${'&quot;$var&quot;'^^}&quot;' eval 'echo "${'"$var"'^^}"'
It was an unfortunate design decision to use the `!` prefix for It was an unfortunate design decision to use the `!` prefix for
indirection, as it introduces parsing ambiguity with other parameter indirection, as it introduces parsing ambiguity with other parameter
@ -220,7 +220,7 @@ of words in the variable while `~~` reverses case for all. Thanks to
<u>**Example: Rename all `*.txt` filenames to lowercase**</u> <u>**Example: Rename all `*.txt` filenames to lowercase**</u>
for file in *.txt; do for file in *.txt; do
mv &quot;$file&quot; &quot;${file,,}&quot; mv "$file" "${file,,}"
done done
<u>**Note:**</u> Case modification is a handy feature you can apply to a <u>**Note:**</u> Case modification is a handy feature you can apply to a
@ -255,7 +255,7 @@ Assume: `array=(This is some Text)`
<!-- --> <!-- -->
* ''echo &quot;${array[2]^^}&quot;'' * ''echo "${array[2]^^}"''
* => ''SOME'' * => ''SOME''
## Variable name expansion ## Variable name expansion
@ -293,7 +293,7 @@ interpreted just like a pattern to describe a filename to match
Example string (*just a quote from a big man*): Example string (*just a quote from a big man*):
MYSTRING=&quot;Be liberal in what you accept, and conservative in what you send&quot; MYSTRING="Be liberal in what you accept, and conservative in what you send"
### From the beginning ### From the beginning
@ -381,7 +381,7 @@ pattern](/syntax/pattern), on expansion time. The matched substring will
be entirely removed and the given string will be inserted. Again some be entirely removed and the given string will be inserted. Again some
example string for the tests: example string for the tests:
MYSTRING=&quot;Be liberal in what you accept, and conservative in what you send&quot; MYSTRING="Be liberal in what you accept, and conservative in what you send"
The two main forms only differ in **the number of slashes** after the The two main forms only differ in **the number of slashes** after the
parameter name: `${PARAMETER/PATTERN/STRING}` and parameter name: `${PARAMETER/PATTERN/STRING}` and
@ -457,7 +457,7 @@ Assume: `array=(This is a text)`
When you use this form, the length of the parameter's value is expanded. When you use this form, the length of the parameter's value is expanded.
Again, a quote from a big man, to have a test text: Again, a quote from a big man, to have a test text:
MYSTRING=&quot;Be liberal in what you accept, and conservative in what you send&quot; MYSTRING="Be liberal in what you accept, and conservative in what you send"
Using echo `${#MYSTRING}`... Using echo `${#MYSTRING}`...
@ -551,7 +551,7 @@ If the `LENGTH` value is negative, it's used as offset from the end of
the string. The expansion happens from the first to the second offset the string. The expansion happens from the first to the second offset
then: then:
echo &quot;${MYSTRING:11:-17}&quot; echo "${MYSTRING:11:-17}"
=\> =\>
`<del>Be liberal </del>in what you accept, and conservative<del> in what you send</del>` `<del>Be liberal </del>in what you accept, and conservative<del> in what you send</del>`
@ -591,16 +591,16 @@ of `PARAMETER`, as if it just was `${PARAMETER}`. If you omit the `:`
(colon), like shown in the second form, the default value is only used (colon), like shown in the second form, the default value is only used
when the parameter was **unset**, not when it was empty. when the parameter was **unset**, not when it was empty.
echo &quot;Your home directory is: ${HOME:-/home/$USER}.&quot; echo "Your home directory is: ${HOME:-/home/$USER}."
echo &quot;${HOME:-/home/$USER} will be used to store your personal data.&quot; echo "${HOME:-/home/$USER} will be used to store your personal data."
If `HOME` is unset or empty, everytime you want to print something If `HOME` is unset or empty, everytime you want to print something
useful, you need to put that parameter syntax in. useful, you need to put that parameter syntax in.
#!/bin/bash #!/bin/bash
read -p &quot;Enter your gender (just press ENTER to not tell us): &quot; GENDER read -p "Enter your gender (just press ENTER to not tell us): " GENDER
echo &quot;Your gender is ${GENDER:-a secret}.&quot; echo "Your gender is ${GENDER:-a secret}."
It will print "Your gender is a secret." when you don't enter the It will print "Your gender is a secret." when you don't enter the
gender. Note that the default value is **used on expansion time**, it is gender. Note that the default value is **used on expansion time**, it is
@ -650,14 +650,14 @@ Example code (please try the example cases yourself):
### CASE 3: An array with only one element, a nullstring ### CASE 3: An array with only one element, a nullstring
array=(&quot;&quot;) array=("")
echo ${array[@]:-This array is NULL or unset} echo ${array[@]:-This array is NULL or unset}
echo ${array[@]-This array is NULL or unset} echo ${array[@]-This array is NULL or unset}
### CASE 4: An array with only two elements, a nullstring and a normal word ### CASE 4: An array with only two elements, a nullstring and a normal word
array=(&quot;&quot; word) array=("" word)
echo ${array[@]:-This array is NULL or unset} echo ${array[@]:-This array is NULL or unset}
echo ${array[@]-This array is NULL or unset} echo ${array[@]-This array is NULL or unset}
@ -675,8 +675,8 @@ unset or null. Equivalent to using a default value, when you omit the
`:` (colon), as shown in the second form, the default value will only be `:` (colon), as shown in the second form, the default value will only be
assigned when the parameter was **unset**. assigned when the parameter was **unset**.
echo &quot;Your home directory is: ${HOME:=/home/$USER}.&quot; echo "Your home directory is: ${HOME:=/home/$USER}."
echo &quot;$HOME will be used to store your personal data.&quot; echo "$HOME will be used to store your personal data."
After the first expansion here (`${HOME:=/home/$USER}`), `HOME` is set After the first expansion here (`${HOME:=/home/$USER}`), `HOME` is set
and usable. and usable.
@ -685,9 +685,9 @@ Let's change our code example from above:
#!/bin/bash #!/bin/bash
read -p &quot;Enter your gender (just press ENTER to not tell us): &quot; GENDER read -p "Enter your gender (just press ENTER to not tell us): " GENDER
echo &quot;Your gender is ${GENDER:=a secret}.&quot; echo "Your gender is ${GENDER:=a secret}."
echo &quot;Ah, in case you forgot, your gender is really: $GENDER&quot; echo "Ah, in case you forgot, your gender is really: $GENDER"
### Assign a default value: Arrays ### Assign a default value: Arrays
@ -707,7 +707,7 @@ This form expands to nothing if the parameter is unset or empty. If it
is set, it does not expand to the parameter's value, **but to some text is set, it does not expand to the parameter's value, **but to some text
you can specify**: you can specify**:
echo &quot;The Java application was installed and can be started.${JAVAPATH:+ NOTE: JAVAPATH seems to be set}&quot; echo "The Java application was installed and can be started.${JAVAPATH:+ NOTE: JAVAPATH seems to be set}"
The above code will simply add a warning if `JAVAPATH` is set (because The above code will simply add a warning if `JAVAPATH` is set (because
it could influence the startup behaviour of that imaginary application). it could influence the startup behaviour of that imaginary application).
@ -718,8 +718,8 @@ flags:
#!/bin/bash #!/bin/bash
read -p &quot;If you want to use special flags, enter them now: &quot; SPECIAL_FLAGS read -p "If you want to use special flags, enter them now: " SPECIAL_FLAGS
echo &quot;The installation of the application is finished${SPECIAL_FLAGS:+ (NOTE: there are special flags set: $SPECIAL_FLAGS)}.&quot; echo "The installation of the application is finished${SPECIAL_FLAGS:+ (NOTE: there are special flags set: $SPECIAL_FLAGS)}."
If you omit the colon, as shown in the second form If you omit the colon, as shown in the second form
(`${PARAMETER+WORD}`), the alternate value will be used if the parameter (`${PARAMETER+WORD}`), the alternate value will be used if the parameter
@ -729,13 +729,13 @@ if variables you need (and that can be empty) are undefined:
# test that with the three stages: # test that with the three stages:
# unset foo # unset foo
# foo=&quot;&quot; # foo=""
# foo=&quot;something&quot; # foo="something"
if [[ ${foo+isset} = isset ]]; then if [[ ${foo+isset} = isset ]]; then
echo &quot;foo is set...&quot; echo "foo is set..."
else else
echo &quot;foo is not set...&quot; echo "foo is not set..."
fi fi
### Use an alternate value: Arrays ### Use an alternate value: Arrays
@ -763,7 +763,7 @@ If the parameter `PARAMETER` is set/non-null, this form will simply
expand it. Otherwise, the expansion of `WORD` will be used as appendix expand it. Otherwise, the expansion of `WORD` will be used as appendix
for an error message: for an error message:
$ echo &quot;The unset parameter is: ${p_unset?not set}&quot; $ echo "The unset parameter is: ${p_unset?not set}"
bash: p_unset: not set bash: p_unset: not set
After printing this message, After printing this message,
@ -785,13 +785,13 @@ are taken into account.
Removing the first 6 characters from a text string: Removing the first 6 characters from a text string:
STRING=&quot;Hello world&quot; STRING="Hello world"
# only print 'Hello' # only print 'Hello'
echo &quot;${STRING%??????}&quot; echo "${STRING%??????}"
# only print 'world' # only print 'world'
echo &quot;${STRING#??????}&quot; echo "${STRING#??????}"
# store it into the same variable # store it into the same variable
STRING=${STRING#??????} STRING=${STRING#??????}
@ -806,7 +806,7 @@ Removing the first 6 characters from a text string:
parameters plus the adjacent expansion are concatenated into a single parameters plus the adjacent expansion are concatenated into a single
argument. As a workaround, each expansion needs to be quoted argument. As a workaround, each expansion needs to be quoted
separately. Unfortunately, this bug took a very long time to separately. Unfortunately, this bug took a very long time to
notice.`~ $ set -- a b c; x=foo; printf '<%s> ' &quot;$@$x&quot; &quot;$*&quot;&quot;$x&quot; &quot;$@&quot;&quot;$x&quot; notice.`~ $ set -- a b c; x=foo; printf '<%s> ' "$@$x" "$*""$x" "$@""$x"
<a b cfoo> <a b cfoo> <a> <b> <cfoo> <a b cfoo> <a b cfoo> <a> <b> <cfoo>
` `
@ -823,14 +823,14 @@ Removing the first 6 characters from a text string:
unquoted, this should be a rare issue. **Always quote unquoted, this should be a rare issue. **Always quote
them**!`touch x 'y z' them**!`touch x 'y z'
for sh in bb {{d,b}a,{m,}k,z}sh; do for sh in bb {{d,b}a,{m,}k,z}sh; do
echo &quot;$sh&quot; echo "$sh"
&quot;$sh&quot; -s a 'b c' d \* </dev/fd/0 "$sh" -s a 'b c' d \* </dev/fd/0
done <<\EOF done <<\EOF
${ZSH_VERSION+:} false && emulate sh ${ZSH_VERSION+:} false && emulate sh
IFS= IFS=
printf '<%s> ' $* printf '<%s> ' $*
echo echo
printf &quot;<%s> &quot; $@ printf "<%s> " $@
echo echo
EOF EOF
``bb ``bb
@ -863,13 +863,13 @@ Removing the first 6 characters from a text string:
the way adjacent arguments are concatenated, when IFS is modified in the way adjacent arguments are concatenated, when IFS is modified in
the middle of expansion through the middle of expansion through
side-effects.`for sh in bb {{d,b}a,po,{m,}k,z}sh; do side-effects.`for sh in bb {{d,b}a,po,{m,}k,z}sh; do
printf '%-4s: ' &quot;$sh&quot; printf '%-4s: ' "$sh"
&quot;$sh&quot; </dev/fd/0 "$sh" </dev/fd/0
done <<\EOF done <<\EOF
${ZSH_VERSION+:} false && emulate sh ${ZSH_VERSION+:} false && emulate sh
set -f -- a b c set -f -- a b c
unset -v IFS unset -v IFS
printf '<%s> ' ${*}${IFS=}${*}${IFS:=-}&quot;${*}&quot; printf '<%s> ' ${*}${IFS=}${*}${IFS:=-}"${*}"
echo echo
EOF EOF
``bb : <a b cabc> <a-b-c> ``bb : <a b cabc> <a-b-c>
@ -913,14 +913,14 @@ Removing the first 6 characters from a text string:
For ranges, Bash evaluates as little as possible, i.e., if the first For ranges, Bash evaluates as little as possible, i.e., if the first
part is out of range, the second won't be evaluated. ksh93 and mksh part is out of range, the second won't be evaluated. ksh93 and mksh
always evaluate the subscript parts even if the parameter is unset. always evaluate the subscript parts even if the parameter is unset.
` $ bash -c 'n=&quot;y[\$(printf yo >&2)1]&quot; m=&quot;y[\$(printf jo >&2)1]&quot;; x=(); echo &quot;${x[@]:n,6:m}&quot;' # No output ` $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=(); echo "${x[@]:n,6:m}"' # No output
$ bash -c 'n=&quot;y[\$(printf yo >&2)1]&quot; m=&quot;y[\$(printf jo >&2)1]&quot;; x=([5]=hi); echo &quot;${x[@]:n,6:m}&quot;' $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([5]=hi); echo "${x[@]:n,6:m}"'
yo yo
$ bash -c 'n=&quot;y[\$(printf yo >&2)1]&quot; m=&quot;y[\$(printf jo >&2)1]&quot;; x=([6]=hi); echo &quot;${x[@]:n,6:m}&quot;' $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([6]=hi); echo "${x[@]:n,6:m}"'
yojo yojo
$ bash -c 'n=&quot;y[\$(printf yo >&2)1]&quot; m=&quot;y[\$(printf jo >&2)1]&quot;; x=12345; echo &quot;${x:n,5:m}&quot;' $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,5:m}"'
yojo yojo
$ bash -c 'n=&quot;y[\$(printf yo >&2)1]&quot; m=&quot;y[\$(printf jo >&2)1]&quot;; x=12345; echo &quot;${x:n,6:m}&quot;' $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,6:m}"'
yo yo
` `
@ -935,12 +935,12 @@ Removing the first 6 characters from a text string:
# Bash # Bash
$ typeset -a a=(meh bleh blerg) b $ typeset -a a=(meh bleh blerg) b
$ IFS=e $ IFS=e
$ printf &quot;<%s> &quot; &quot;${b[@]-&quot;${a[@]}&quot; &quot;${a[@]}&quot;}&quot;; echo # The entire PE is quoted so Bash considers the inner quotes redundant. $ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}"; echo # The entire PE is quoted so Bash considers the inner quotes redundant.
<meh> <bleh> <blerg meh> <bleh> <blerg> <meh> <bleh> <blerg meh> <bleh> <blerg>
$ printf &quot;<%s> &quot; &quot;${b[@]-${a[@]} ${a[@]}}&quot;; echo # The outer quotes cause the inner expansions to be considered quoted. $ printf "<%s> " "${b[@]-${a[@]} ${a[@]}}"; echo # The outer quotes cause the inner expansions to be considered quoted.
<meh> <bleh> <blerg meh> <bleh> <blerg> <meh> <bleh> <blerg meh> <bleh> <blerg>
$ b=(meep beep) $ b=(meep beep)
$ printf &quot;<%s> &quot; &quot;${b[@]-&quot;${a[@]}&quot; &quot;${a[@]}&quot;}&quot; &quot;${b[@]-${a[@]} ${a[@]}}&quot;; echo # Again no surprises. Outer quotes quote everything recursively. $ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}" "${b[@]-${a[@]} ${a[@]}}"; echo # Again no surprises. Outer quotes quote everything recursively.
<meep> <beep> <meep> <beep> <meep> <beep> <meep> <beep>
Now lets see what can happen if we leave the outside unquoted. Now lets see what can happen if we leave the outside unquoted.
@ -948,9 +948,9 @@ Now lets see what can happen if we leave the outside unquoted.
# Bash # Bash
$ typeset -a a=(meh bleh blerg) b $ typeset -a a=(meh bleh blerg) b
$ IFS=e $ IFS=e
$ printf &quot;<%s> &quot; ${b[@]-&quot;${a[@]}&quot; &quot;${a[@]}&quot;}; echo # Inner quotes make inner expansions quoted. $ printf "<%s> " ${b[@]-"${a[@]}" "${a[@]}"}; echo # Inner quotes make inner expansions quoted.
<meh> <bleh> <blerg meh> <bleh> <blerg> <meh> <bleh> <blerg meh> <bleh> <blerg>
$ printf &quot;<%s> &quot; ${b[@]-${a[@]} ${a[@]}}; echo' # No quotes at all wordsplits / globs, like you'd expect. $ printf "<%s> " ${b[@]-${a[@]} ${a[@]}}; echo' # No quotes at all wordsplits / globs, like you'd expect.
<m> <h> <bl> <h> <bl> <rg m> <h> <bl> <h> <bl> <rg> <m> <h> <bl> <h> <bl> <rg m> <h> <bl> <h> <bl> <rg>
This all might be intuitive, and is the most common implementation, but This all might be intuitive, and is the most common implementation, but
@ -976,7 +976,7 @@ Consider the following (only slightly far-fetched) code:
# What do you think the programmer expected to happen here? # What do you think the programmer expected to happen here?
# What do you think will actually happen... # What do you think will actually happen...
&quot;${someCmd[@]-&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;}&quot; arg5 "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5
This final line is perhaps not the most obvious, but I've run into cases This final line is perhaps not the most obvious, but I've run into cases
were this type of logic can be desirable and realistic. We can deduce were this type of logic can be desirable and realistic. We can deduce
@ -996,20 +996,20 @@ outer expansion correctly without word-splitting or globbing is to quote
it. Bash will actually expand the command as one of these: it. Bash will actually expand the command as one of these:
# The quoted PE produces a correct result here... # The quoted PE produces a correct result here...
$ bash -c 'typeset -a someCmd=(myCmd arg1 &quot;arg2 yay!&quot; &quot;third*arg*&quot; 4); printf &quot;<%s> &quot; &quot;${someCmd[@]-&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;}&quot; arg5; echo' $ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo'
<myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5> <myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5>
# ...but in the opposite case the first 3 arguments are glued together. There are no workarounds. # ...but in the opposite case the first 3 arguments are glued together. There are no workarounds.
$ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf &quot;<%s> &quot; &quot;${someCmd[@]-&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;}&quot; arg5; echo' $ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo'
<mycommand arg2 arg3> <arg4> <arg5> <mycommand arg2 arg3> <arg4> <arg5>
# UNLESS! we unquote the outer expansion allowing the inner quotes to # UNLESS! we unquote the outer expansion allowing the inner quotes to
# affect the necessary parts while allowing word-splitting to split the literals: # affect the necessary parts while allowing word-splitting to split the literals:
$ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf &quot;<%s> &quot; ${someCmd[@]-&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;} arg5; echo' $ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo'
<mycommand> <arg2> <arg3> <arg4> <arg5> <mycommand> <arg2> <arg3> <arg4> <arg5>
# Success!!! # Success!!!
$ bash -c 'typeset -a someCmd=(myCmd arg1 &quot;arg2 yay!&quot; &quot;third*arg*&quot; 4); printf &quot;<%s> &quot; ${someCmd[@]-&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;} arg5; echo' $ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo'
<myCmd> <arg1> <arg2> <yay!> <third*arg*> <4> <arg5> <myCmd> <arg1> <arg2> <yay!> <third*arg*> <4> <arg5>
# ...Ah f^^k. (again, no workaround possible.) # ...Ah f^^k. (again, no workaround possible.)
@ -1027,10 +1027,10 @@ All we need to do is add two extra double-quotes:
# ksh93 passing the two failed tests from above: # ksh93 passing the two failed tests from above:
$ ksh -c 'otherArgs=(arg3 arg4); someOtherCmd=&quot;mycommand&quot;; printf &quot;<%s> &quot; &quot;${someCmd[@]-&quot;&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;&quot;}&quot; arg5; echo' $ ksh -c 'otherArgs=(arg3 arg4); someOtherCmd="mycommand"; printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo'
<mycommand> <arg2> <arg3> <arg4> <arg5> <mycommand> <arg2> <arg3> <arg4> <arg5>
$ ksh -c 'typeset -a someCmd=(myCmd arg1 &quot;arg2 yay!&quot; &quot;third*arg*&quot; 4); printf &quot;<%s> &quot; &quot;${someCmd[@]-&quot;&quot;$someOtherCmd&quot; arg2 &quot;${otherArgs[@]}&quot;&quot;}&quot; arg5; echo' $ ksh -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo'
<myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5> <myCmd> <arg1> <arg2 yay!> <third*arg*> <4> <arg5>
This can be used to control the quote state of any part of any expansion This can be used to control the quote state of any part of any expansion

View File

@ -21,18 +21,18 @@ related to the quote characters passed as text to the command line! The
syntax quotes are removed before the command is called! Example: syntax quotes are removed before the command is called! Example:
### NO NO NO: this passes three strings: ### NO NO NO: this passes three strings:
### (1) &quot;my ### (1) "my
### (2) multiword ### (2) multiword
### (3) argument&quot; ### (3) argument"
MYARG=&quot;\&quot;my multiword argument\&quot;&quot; MYARG="\"my multiword argument\""
somecommand $MYARG somecommand $MYARG
### THIS IS NOT (!) THE SAME AS ### ### THIS IS NOT (!) THE SAME AS ###
command &quot;my multiword argument&quot; command "my multiword argument"
### YOU NEED ### ### YOU NEED ###
MYARG=&quot;my multiword argument&quot; MYARG="my multiword argument"
command &quot;$MYARG&quot; command "$MYARG"
## Per-character escaping ## Per-character escaping
@ -41,7 +41,7 @@ general, a character that has a special meaning to Bash, like the
dollar-sign (`$`) can be masked to not have a special meaning using the dollar-sign (`$`) can be masked to not have a special meaning using the
backslash: backslash:
echo \$HOME is set to \&quot;$HOME\&quot; echo \$HOME is set to \"$HOME\"
- `\$HOME` won't expand because it's not in variable-expansion syntax - `\$HOME` won't expand because it's not in variable-expansion syntax
anymore anymore
@ -81,12 +81,12 @@ Inside a weak-quoted string there's **no special interpretion of**:
Everything else, especially [parameter expansion](/syntax/pe), is Everything else, especially [parameter expansion](/syntax/pe), is
performed! performed!
ls -l &quot;*&quot; ls -l "*"
Will not be expanded. `ls` gets the literal `*` as argument. It will, Will not be expanded. `ls` gets the literal `*` as argument. It will,
unless you have a file named `*`, spit out an error. unless you have a file named `*`, spit out an error.
echo &quot;Your PATH is: $PATH&quot; echo "Your PATH is: $PATH"
Will work as expected. `$PATH` is expanded, because it's double (weak) Will work as expected. `$PATH` is expanded, because it's double (weak)
quoted. quoted.
@ -126,7 +126,7 @@ to get the character "`'`" as literal text:
echo 'Here'\''s my test...' echo 'Here'\''s my test...'
# ALTERNATIVE: It's also possible to mix-and-match quotes for readability: # ALTERNATIVE: It's also possible to mix-and-match quotes for readability:
echo &quot;Here's my test&quot; echo "Here's my test"
## ANSI C like strings ## ANSI C like strings
@ -174,7 +174,7 @@ compatibility" features).
A dollar-sign followed by a double-quoted string, for example A dollar-sign followed by a double-quoted string, for example
echo $&quot;generating database...&quot; echo $"generating database..."
means I18N. If there is a translation available for that string, it is means I18N. If there is a translation available for that string, it is
used instead of the given text. If not, or if the locale is `C`/`POSIX`, used instead of the given text. If not, or if the locale is `C`/`POSIX`,
@ -200,11 +200,11 @@ documentation](http://www.gnu.org/software/gettext/manual/html_node/bash.html)
The [classic for loop](/syntax/ccmd/classic_for) uses a list of words to The [classic for loop](/syntax/ccmd/classic_for) uses a list of words to
iterate through. The list can also be in a variable: iterate through. The list can also be in a variable:
mylist=&quot;DOG CAT BIRD HORSE&quot; mylist="DOG CAT BIRD HORSE"
**<u>WRONG</u>** way to iterate through this list: **<u>WRONG</u>** way to iterate through this list:
for animal in &quot;$mylist&quot;; do for animal in "$mylist"; do
echo $animal echo $animal
done done
@ -235,7 +235,7 @@ writing this as a test command it would be:
When you compare variables, it's wise to quote them. Let's create a test When you compare variables, it's wise to quote them. Let's create a test
string with spaces: string with spaces:
mystring=&quot;my string&quot; mystring="my string"
And now check that string against the word "testword": And now check that string against the word "testword":
@ -251,7 +251,7 @@ Which is wrong, because `my` and `string` are two separate arguments.
So what you really want to do is: So what you really want to do is:
[ &quot;$mystring&quot; = testword ] # RIGHT! [ "$mystring" = testword ] # RIGHT!
test 'my string' = testword test 'my string' = testword

View File

@ -36,7 +36,7 @@ Whenever you **reference** a descriptor, to point to its current target
file, then you use a "`&`" followed by a the descriptor number: file, then you use a "`&`" followed by a the descriptor number:
# this executes the echo-command and redirects its normal output (stdout) to the standard error target # this executes the echo-command and redirects its normal output (stdout) to the standard error target
echo &quot;There was an error&quot; 1>&2 echo "There was an error" 1>&2
The redirection operation can be **anywhere** in a simple command, so The redirection operation can be **anywhere** in a simple command, so
these examples are equivalent: these examples are equivalent:
@ -166,7 +166,7 @@ substitutions and expansions are performed in the here-document data:
You can avoid that by quoting the tag: You can avoid that by quoting the tag:
cat <<&quot;EOF&quot; cat <<"EOF"
This won't be expanded: $PATH This won't be expanded: $PATH
EOF EOF
@ -191,7 +191,7 @@ end-of-file is seen before the tag is reached.\</wrap\>
The here-strings are a variation of the here-documents. The word `WORD` The here-strings are a variation of the here-documents. The word `WORD`
is taken for the input redirection: is taken for the input redirection:
cat <<< &quot;Hello world... $NAME is here...&quot; cat <<< "Hello world... $NAME is here..."
Just beware to quote the `WORD` if it contains spaces. Otherwise the Just beware to quote the `WORD` if it contains spaces. Otherwise the
rest will be given as normal parameters. rest will be given as normal parameters.

View File

@ -1238,7 +1238,7 @@ current mailfile.
Example content: Example content:
/var/mail/bfox?&quot;You have mail&quot;:~/shell-mail?&quot;$_ has mail!&quot; /var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!"
### OPTERR ### OPTERR

View File

@ -59,7 +59,7 @@ context of command-splitting, which this section is about, it doesn't
matter which kind of quoting you use: weak quoting or strong quoting, matter which kind of quoting you use: weak quoting or strong quoting,
both cause Bash to not treat spaces as special characters: both cause Bash to not treat spaces as special characters:
$ echo &quot;Hello little world&quot; $ echo "Hello little world"
Hello little world Hello little world
$ echo 'Hello little world' $ echo 'Hello little world'
@ -80,7 +80,7 @@ spaces in them:
$ cat test\ file $ cat test\ file
m00! m00!
$ cat &quot;test file&quot; $ cat "test file"
m00! m00!
If you enter that on the command line with Tab completion, that will If you enter that on the command line with Tab completion, that will
@ -103,7 +103,7 @@ second type of word splitting comes in - several expansions undergo
Imagine you have a filename stored in a variable: Imagine you have a filename stored in a variable:
MYFILE=&quot;test file&quot; MYFILE="test file"
When this variable is used, its occurance will be replaced by its When this variable is used, its occurance will be replaced by its
content. content.
@ -116,7 +116,7 @@ Though this is another step where spaces make things difficult,
**quoting** is used to work around the difficulty. Quotes also affect **quoting** is used to work around the difficulty. Quotes also affect
word splitting: word splitting:
$ cat &quot;$MYFILE&quot; $ cat "$MYFILE"
m00! m00!
## Example ## Example
@ -124,7 +124,7 @@ word splitting:
Let's follow an unquoted command through these steps, assuming that the Let's follow an unquoted command through these steps, assuming that the
variable is set: variable is set:
MYFILE=&quot;THE FILE.TXT&quot; MYFILE="THE FILE.TXT"
and the first review is: and the first review is:
@ -149,7 +149,7 @@ splitting](/syntax/expansion/wordsplit) on the results:
Now let's imagine we quoted `$MYFILE`, the command line now looks like: Now let's imagine we quoted `$MYFILE`, the command line now looks like:
echo The file is named &quot;$MYFILE&quot; echo The file is named "$MYFILE"
| Word splitting after substitution (quoted!): | | | | | | | Word splitting after substitution (quoted!): | | | | | |
|----------------------------------------------|--------|--------|--------|---------|----------------| |----------------------------------------------|--------|--------|--------|---------|----------------|