From 39591d2ff9fcfac087b532e819dd7cac92a4b2f0 Mon Sep 17 00:00:00 2001 From: Rawiri Blundell Date: Mon, 24 Apr 2023 23:27:29 +1200 Subject: [PATCH] Correct html quote char --- archive_crawler | 7 +- bash4.md | 6 +- commands/builtin/caller.md | 4 +- commands/builtin/declare.md | 16 ++--- commands/builtin/eval.md | 28 ++++---- commands/builtin/exec.md | 4 +- commands/builtin/export.md | 2 +- commands/builtin/let.md | 14 ++-- commands/builtin/mapfile.md | 18 ++--- commands/builtin/printf.md | 44 ++++++------ commands/builtin/read.md | 52 +++++++------- commands/builtin/shift.md | 2 +- commands/builtin/unset.md | 34 ++++----- commands/classictest.md | 82 ++++++++++----------- howto/calculate-dc.md | 6 +- howto/collapsing_functions.md | 30 ++++---- howto/conffile.md | 34 ++++----- howto/dissectabadoneliner.md | 20 +++--- howto/edit-ed.md | 14 ++-- howto/getopts_tutorial.md | 18 ++--- howto/mutex.md | 58 +++++++-------- howto/pax.md | 6 +- howto/redirection_tutorial.md | 8 +-- howto/testing-your-scripts.md | 8 +-- misc/readthesourceluke.md | 2 +- scripting/bashchanges.md | 2 +- scripting/basics.md | 38 +++++----- scripting/debuggingtips.md | 28 ++++---- scripting/newbie_traps.md | 20 +++--- scripting/nonportable.md | 14 ++-- scripting/posparams.md | 66 ++++++++--------- scripting/processtree.md | 4 +- scripting/style.md | 22 +++--- scripting/terminalcodes.md | 28 ++++---- syntax/arith_expr.md | 14 ++-- syntax/arrays.md | 68 +++++++++--------- syntax/basicgrammar.md | 14 ++-- syntax/ccmd/c_for.md | 12 ++-- syntax/ccmd/case.md | 28 ++++---- syntax/ccmd/classic_for.md | 26 +++---- syntax/ccmd/grouping_plain.md | 16 ++--- syntax/ccmd/grouping_subshell.md | 6 +- syntax/ccmd/if_clause.md | 12 ++-- syntax/expansion/arith.md | 6 +- syntax/expansion/brace.md | 16 ++--- syntax/expansion/cmdsubst.md | 14 ++-- syntax/expansion/globs.md | 12 ++-- syntax/expansion/intro.md | 4 +- syntax/expansion/proc_subst.md | 10 +-- syntax/expansion/tilde.md | 6 +- syntax/grammar/parser_exec.md | 2 +- syntax/keywords/coproc.md | 30 ++++---- syntax/pe.md | 120 +++++++++++++++---------------- syntax/quoting.md | 30 ++++---- syntax/redirection.md | 6 +- syntax/shellvars.md | 2 +- syntax/words.md | 12 ++-- 57 files changed, 605 insertions(+), 600 deletions(-) diff --git a/archive_crawler b/archive_crawler index 7337110..674e577 100644 --- a/archive_crawler +++ b/archive_crawler @@ -107,7 +107,12 @@ scrape_targets() { # This should remove everything above and below our desired Dokuwiki Markup # We also take the opportunity to convert some HTML chars extract_markup() { - sed -e '1,/name="sectok"/d' -e '/<\/textarea>/,$d' -e 's/>/>/g' -e 's/<//,$d' \ + -e 's/>/>/g' \ + -e 's/</ function sum { @@ -123,10 +123,10 @@ variable whose name is expanded on the RHS. shift typeset IFS=+ _arrx _result=0 - for _arrx in "$@"; do # Demonstrate the special property of "for" on a nameref. + for _arrx in "$@"; do # Demonstrate the special property of "for" on a nameref. typeset -n _arr=$_arrx (( _result += ${_arr[*]} )) - printf '%s = %d\n' "${!_result}" "$_result" # 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 } diff --git a/commands/builtin/eval.md b/commands/builtin/eval.md index 176a117..aec3f25 100644 --- a/commands/builtin/eval.md +++ b/commands/builtin/eval.md @@ -27,7 +27,7 @@ the `eval` command below it. ... arbitrary bash code here ... EOF - eval "$myCode" + eval "$myCode" ### 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. main() { - local fun='() { echo "$FUNCNAME"; }' x + local fun='() { echo "$FUNCNAME"; }' x for x in {f..n}; do - eval "${x}${fun}" + eval "${x}${fun}" done - "$@" + "$@" } - main "$@" + main "$@" ### 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 through printf requiring another `eval` to peel off the escaping again. - while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do - printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}" + while (( ++n <= 5 )) || ! evalBall="eval $evalBall"; do + printf -v evalBall 'eval %q' "printf $n;${evalBall-printf '0\n'}" done $evalBall @@ -78,19 +78,19 @@ application](http://en.wikipedia.org/wiki/Partial_application) using `eval`. function partial { - eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }' + eval shift 2 \; function "$1" \{ "$2" "$(printf '%q ' "${@:3}")" '"$@"; }' } function repeat { [[ $1 == +([0-9]) ]] || return typeset n while ((n++ < $1)); do - "${@:2}" + "${@:2}" done } partial print3 repeat 3 printf '%s ' # Create a new function named print3 - print3 hi # Print "hi" 3 times + print3 hi # Print "hi" 3 times echo 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> ' "${a[@]}"; echo ) # Only works in Bash. + $ ( eval a=( a b\\ c d ); printf '<%s> ' "${a[@]}"; echo ) # Only works in Bash. - $ ( 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. + $ ( 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 `(' - $ ( x=a; eval "$x"'=( a b\ c d )'; printf '<%s> ' "${a[@]}"; 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. 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 expansion](syntax/expansion/glob). - $ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" ) + $ ( set -x; touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)'; eval x+=(*); echo "${x[@]}" ) + touch 'x+=(\[[123]\]=*)' 'x+=([3]=yo)' + eval 'x+=(\[[123]\]=*)' 'x+=([3]=yo)' ++ x+=(\[[123]\]=*) diff --git a/commands/builtin/exec.md b/commands/builtin/exec.md index 9dd0476..bb95610 100644 --- a/commands/builtin/exec.md +++ b/commands/builtin/exec.md @@ -38,12 +38,12 @@ shell without executing any program. ``` bash myprog=/bin/ls -echo "This is the wrapper script, it will exec $myprog" +echo "This is the wrapper script, it will exec $myprog" # do some vodoo here, probably change the arguments etc. # well, stuff a wrapper is there for -exec "$myprog" "$@" +exec "$myprog" "$@" ``` ### Open a file as input for the script diff --git a/commands/builtin/export.md b/commands/builtin/export.md index 87d4115..3a935b7 100644 --- a/commands/builtin/export.md +++ b/commands/builtin/export.md @@ -35,7 +35,7 @@ An argument of `--` disables further option processing. Set the display to use when launching a GUI application (useful during SSH sessions): - export DISPLAY=":0" + export DISPLAY=":0" Set your default text editor (e.g. SublimeText): diff --git a/commands/builtin/let.md b/commands/builtin/let.md index 4faa45e..c998a40 100644 --- a/commands/builtin/let.md +++ b/commands/builtin/let.md @@ -30,15 +30,15 @@ for arithmetic). For this reason, **the [arithmetic compound command](/syntax/ccmd/arithmetic_eval) should generally be preferred over `let`**. - $ let 'b = a' "(a += 3) + $((a = 1)), b++" - $ echo "$a - $b - $?" + $ let 'b = a' "(a += 3) + $((a = 1)), b++" + $ echo "$a - $b - $?" 4 - 2 - 0 Is equivalent to the [arithmetic evaluation compound command](/syntax/ccmd/arithmetic_eval): $ (( b = a, (a += 3) + $((a = 1)), b++ )) - $ echo "$a - $b - $?" + $ echo "$a - $b - $?" 4 - 2 - 0 \ 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. ~ $ ( y=1+1 let x=y; declare -p x y ) - declare -- x="2" + declare -- x="2" bash: declare: y: not found ~ $ ( y=1+1 let x=y++; declare -p x y ) - declare -- x="2" - declare -- y="3" + declare -- x="2" + declare -- y="3" This can be useful in certain situations where a temporary variable is needed. @@ -70,7 +70,7 @@ needed. ## Portability considerations - the `let` command is not specified by POSIX(r). The portable - alternative is: `[ "$(( ))" -ne 0 ]`. To make + alternative is: `[ "$(( ))" -ne 0 ]`. To make portable scripts simpler and cleaner, `let` can be defined as: `# POSIX let() { diff --git a/commands/builtin/mapfile.md b/commands/builtin/mapfile.md index f9bf9ed..bdae700 100644 --- a/commands/builtin/mapfile.md +++ b/commands/builtin/mapfile.md @@ -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 business. - # eix --only-names -IC x11-drivers | { mapfile -t args; emerge -av1 "${args[@]}" <&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 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 allows for easy access to the arguments with no ugly "code in a string". - $ foo() { echo "|$1|"; }; mapfile -n 11 -c 2 -C 'foo' &$(( (${#x[@]} % 2) + 3 )) printf -- "%.sprefix %s"' 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} prefix input1 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 use in real code). - $ 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[@]}"; } + $ 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 input3 prefix input5 @@ -154,7 +154,7 @@ and returns the record. #!/usr/bin/env bash showRecord() { - printf 'key[%d] = %d, %d\n' "$1" "${vals[@]:keys[$1]*2:2}" + printf 'key[%d] = %d, %d\n' "$1" "${vals[@]:keys[$1]*2:2}" } parseRecords() { @@ -167,17 +167,17 @@ and returns the record. local n _f - mapfile -tc2 -C _f "$1" - eval "$1"'=("${'"$1"'[@]##*:}")' # Return the array with some modification + mapfile -tc2 -C _f "$1" + eval "$1"'=("${'"$1"'[@]##*:}")' # Return the array with some modification } main() { local -a keys vals parseRecords vals - showRecord "$1" + showRecord "$1" } - main "$1" <<-"EOF" + main "$1" <<-"EOF" fabric.domain:123 routex:1 routey:2 diff --git a/commands/builtin/printf.md b/commands/builtin/printf.md index 62d91a6..cdbc0b7 100644 --- a/commands/builtin/printf.md +++ b/commands/builtin/printf.md @@ -41,7 +41,7 @@ formatstring may point to are given after that, here, indicated by Thus, a typical `printf`-call looks like: - printf "Surname: %s\nName: %s\n" "$SURNAME" "$FIRSTNAME" + printf "Surname: %s\nName: %s\n" "$SURNAME" "$FIRSTNAME" where `"Surname: %s\nName: %s\n"` is the format specification, and the 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 this opens up the possibility of an easy code injection vulnerability. - $ var='-vx[$(echo hi >&2)]'; printf "$var" hi; declare -p x + $ var='-vx[$(echo hi >&2)]'; printf "$var" hi; declare -p x hi - declare -a x='([0]="hi")' + declare -a x='([0]="hi")' ...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 @@ -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 introductory `%` and the character that specifies the format: - printf "%50s\n" "This field is 50 characters wide..." + printf "%50s\n" "This field is 50 characters wide..." #### Field and printing modifiers @@ -191,7 +191,7 @@ The precision for a floating- or double-number can be specified by using `` is an asterisk (`*`), the precision is read from the argument that precedes the number to print, like (prints 4,3000000000): - printf "%.*f\n" 10 4,3 + printf "%.*f\n" 10 4,3 The format `.*N` to specify the N'th argument for precision does not 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 - printf '%3d | %04o | 0x%02x\n' "$x" "$x" "$x" + printf '%3d | %04o | 0x%02x\n' "$x" "$x" "$x" done ### 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 digits, ...): - the_mac="0:13:ce:7:7a:ad" + the_mac="0:13:ce:7:7a:ad" # lowercase hex digits - the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/ 0x})" + the_mac="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${the_mac//:/ 0x})" # or the uppercase-digits variant - the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})" + the_mac="$(printf "%02X:%02X:%02X:%02X:%02X:%02X" 0x${the_mac//:/ 0x})" ### 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: - printf "%b\n" "$*" + printf "%b\n" "$*" Solaris `/usr/ucb/echo` is equivalent to: - if [ "X$1" = "X-n" ] + if [ "X$1" = "X-n" ] then shift - printf "%s" "$*" + printf "%s" "$*" else - printf "%s\n" "$*" + printf "%s\n" "$*" fi ### prargs Implementation @@ -311,14 +311,14 @@ Solaris `/usr/ucb/echo` is equivalent to: Working off the replacement echo, here is a terse implementation of prargs: - printf '"%b"\n' "$0" "$@" | nl -v0 -s": " + printf '"%b"\n' "$0" "$@" | nl -v0 -s": " ### repeating a character (for example to print a line) A small trick: Combining printf and parameter expansion to draw a line length=40 - printf -v line '%*s' "$length" + printf -v line '%*s' "$length" echo ${line// /-} 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. - $ echo "Foo" | awk '{ printf "%s\n" $1 }' + $ echo "Foo" | awk '{ printf "%s\n" $1 }' awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string `%s 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 correct awk syntax. - $ echo "Foo" | awk '{ printf( "%s\n", $1 ) }' + $ echo "Foo" | awk '{ printf( "%s\n", $1 ) }' Foo 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 readability. - echo "Foo" | awk '{ system( "printf \"%s\\n \" \"" $1 "\"" ) }' + echo "Foo" | awk '{ system( "printf \"%s\\n \" \"" $1 "\"" ) }' Foo ## Differences from C, and portability considerations @@ -395,7 +395,7 @@ readability. modifiers specified by POSIX during format string parsing. ``` c|builtins/printf.def -#define LENMODS "hjlLtz" +#define LENMODS "hjlLtz" ... /* skip possible format modifiers */ modstart = fmt; @@ -427,15 +427,15 @@ fmt++; shift nameref x=$1 shift - x=$(command printf "$@") + x=$(command printf "$@") ;; *) - command printf "$@" + command printf "$@" esac } builtin cut print $$ - printf -v 'foo[2]' '%d\n' "$(cut -d ' ' -f 1 /proc/self/stat)" + printf -v 'foo[2]' '%d\n' "$(cut -d ' ' -f 1 /proc/self/stat)" typeset -p foo # 22461 # typeset -a foo=([2]=22461) diff --git a/commands/builtin/read.md b/commands/builtin/read.md index 947f2fd..3e1d5da 100644 --- a/commands/builtin/read.md +++ b/commands/builtin/read.md @@ -27,8 +27,8 @@ the line as it was read, without stripping pre- and postfix spaces and other things! while read -r; do - printf '"%s"\n' "$REPLY" - done <<<" a line with prefix and postfix space " + printf '"%s"\n' "$REPLY" + done <<<" a line with prefix and postfix space " \ @@ -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: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:38 geirha # read x y <<< 'foo\ bar baz'; echo "<$x><$y>" + 2012-05-23 13:49:38 geirha # read x y <<< 'foo\ bar baz'; echo "<$x><$y>" 2012-05-23 13:49:40 shbot geirha: 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: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:52:04 geirha echo \" outputs a " , read x <<< '\"' reads a " + 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:46 * ormaaj struggles to think of a point to that... 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() { while read -r; do - printf "%s\n" "$REPLY" - done <"$1" + printf "%s\n" "$REPLY" + done <"$1" } **Note:** Here, `read -r` and the default `REPLY` is used, @@ -145,7 +145,7 @@ Remember the MSDOS `pause` command? Here's something similar: pause() { local dummy - read -s -r -p "Press any key to continue..." -n 1 dummy + read -s -r -p "Press any key to continue..." -n 1 dummy } Notes: @@ -160,14 +160,14 @@ Notes: Read can be used to split a string: - var="one two three" - read -r col1 col2 col3 <<< "$var" - printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3" + var="one two three" + read -r col1 col2 col3 <<< "$var" + printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3" Take care that you cannot use a pipe: - echo "$var" | read col1 col2 col3 # does not work! - printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3" + echo "$var" | read col1 col2 col3 # does not work! + printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3" Why? because the commands of the pipe run in subshells that cannot 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 variable get the remaining of the line: - read col1 col2 col3 <<< "one two three four" - printf "%s\n" "$col3" #prints three four + read col1 col2 col3 <<< "one two three four" + printf "%s\n" "$col3" #prints three four #### 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* [IFS](/syntax/shellvars#IFS), the Internal Field Separator. - IFS=":" read -r col1 col2 <<< "hello:world" - printf "col1: %s col2: %s\n" "$col1" "$col2" + IFS=":" read -r col1 col2 <<< "hello:world" + printf "col1: %s col2: %s\n" "$col1" "$col2" 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 @@ -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 (space or tab), the fields are separated by **exactly** one character: - IFS=":" read -r col1 col2 col3 <<< "hello::world" - printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3" + IFS=":" read -r col1 col2 col3 <<< "hello::world" + printf "col1: %s col2: %s col3 %s\n" "$col1" "$col2" "$col3" See how the `::` in the middle infact defines an additional *empty field*. @@ -207,13 +207,13 @@ field*. The fields are separated by exactly one character, but the character can be different between each field: - IFS=":|@" read -r col1 col2 col3 col4 <<< "hello:world|in@bash" - printf "col1: %s col2: %s col3 %s col4 %s\n" "$col1" "$col2" "$col3" "$col4" + IFS=":|@" read -r col1 col2 col3 col4 <<< "hello:world|in@bash" + printf "col1: %s col2: %s col3 %s col4 %s\n" "$col1" "$col2" "$col3" "$col4" ### Are you sure? asksure() { - echo -n "Are you sure (Y/N)? " + echo -n "Are you sure (Y/N)? " while read -r -n 1 -s answer; do if [[ $answer = [YyNn] ]]; then [[ $answer = [Yy] ]] && retval=0 @@ -229,16 +229,16 @@ be different between each field: ### using it if asksure; then - echo "Okay, performing rm -rf / then, master...." + echo "Okay, performing rm -rf / then, master...." else - echo "Pfff..." + echo "Pfff..." fi ### Ask for a path with a default value **Note:** The `-i` option was introduced with Bash 4 - read -e -p "Enter the path to the file: " -i "/usr/local/etc/" 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. @@ -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 date/time string are recognized correctly. - datetime="2008:07:04 00:34:45" - IFS=": " read -r year month day hour minute second <<< "$datetime" + datetime="2008:07:04 00:34:45" + IFS=": " read -r year month day hour minute second <<< "$datetime" ## Portability considerations diff --git a/commands/builtin/shift.md b/commands/builtin/shift.md index 0efddd7..a4920f3 100644 --- a/commands/builtin/shift.md +++ b/commands/builtin/shift.md @@ -59,7 +59,7 @@ There are no options. [shift_verbose](internals/shell_options#shift_verbose) [shopt](commands/builtin/shopt) option is enabled. Ksh93, pdksh, posh, mksh, and dash, all throw useless fatal shell - errors.`$ dash -c 'f() { if shift; then echo "$1"; else echo "no args"; 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 ` In most shells, you can work around this problem using the [command](/commands/builtin/command) builtin to suppress fatal errors diff --git a/commands/builtin/unset.md b/commands/builtin/unset.md index 41e34da..cb750fd 100644 --- a/commands/builtin/unset.md +++ b/commands/builtin/unset.md @@ -63,12 +63,12 @@ Here's a demonstration of this behavior. # Direct recursion depth. # Search up the stack for the first non-FUNCNAME[1] and count how deep we are. callDepth() { - # Strip "main" off the end of FUNCNAME[@] if current function is named "main" and - # Bash added an extra "main" for non-interactive scripts. - if [[ main == !(!("${FUNCNAME[1]}")|!("${FUNCNAME[-1]}")) && $- != *i* ]]; then - local -a 'fnames=("${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}")' + # Strip "main" off the end of FUNCNAME[@] if current function is named "main" and + # Bash added an extra "main" for non-interactive scripts. + if [[ main == !(!("${FUNCNAME[1]}")|!("${FUNCNAME[-1]}")) && $- != *i* ]]; then + local -a 'fnames=("${FUNCNAME[@]:1:${#FUNCNAME[@]}-2}")' else - local -a 'fnames=("${FUNCNAME[@]:1}")' + local -a 'fnames=("${FUNCNAME[@]:1}")' fi if (( ! ${#fnames[@]} )); then @@ -86,7 +86,7 @@ Here's a demonstration of this behavior. # This function is the magic stack walker. unset2() { - unset -v -- "$@" + unset -v -- "$@" } f() { @@ -97,11 +97,11 @@ Here's a demonstration of this behavior. f else trap 'declare -p a' DEBUG - unset2 a # declare -- a="5" - unset a a # declare -- a="4" - unset a # declare -- a="2" + unset2 a # declare -- a="5" + unset a a # declare -- a="4" + unset a # declare -- a="2" unset a # ./unset-tests: line 44: declare: a: not found - : # declare -- a="global scope yo" + : # declare -- a="global scope yo" fi } @@ -112,11 +112,11 @@ Here's a demonstration of this behavior. output: - declare -- a="5" - declare -- a="4" - declare -- a="2" + declare -- a="5" + declare -- a="4" + declare -- a="2" ./unset-tests: line 44: declare: a: not found - declare -- a="global scope yo" + declare -- a="global scope yo" Some things to observe: @@ -143,15 +143,15 @@ Like several other Bash builtins that take parameter names, unset expands its arguments. ~ $ ( a=({a..d}); unset 'a[2]'; declare -p a ) - declare -a a='([0]="a" [1]="b" [3]="d")' + declare -a a='([0]="a" [1]="b" [3]="d")' As usual in such cases, it's important to quote the args to avoid accidental results such as globbing. - ~ $ ( a=({a..d}) b=a c=d d=1; set -x; unset "${b}["{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]' + declare -p a - declare -a a='([0]="a" [3]="d")' + declare -a a='([0]="a" [3]="d")' Of course hard to follow indirection is still possible whenever arithmetic is involved, also as shown above, even without extra diff --git a/commands/classictest.md b/commands/classictest.md index 170f8f7..888a4fe 100644 --- a/commands/classictest.md +++ b/commands/classictest.md @@ -15,9 +15,9 @@ here by using the command in an if-statement: # test if /etc/passwd exists if test -e /etc/passwd; then - echo "Alright man..." >&2 + echo "Alright man..." >&2 else - echo "Yuck! Where is it??" >&2 + echo "Yuck! Where is it??" >&2 exit 1 fi @@ -37,9 +37,9 @@ Let's rewrite the above example to use it: # test if /etc/passwd exists if [ -e /etc/passwd ]; then - echo "Alright man..." >&2 + echo "Alright man..." >&2 else - echo "Yuck! Where is it??" >&2 + echo "Yuck! Where is it??" >&2 exit 1 fi @@ -53,12 +53,12 @@ some of your music files: #!/bin/bash - mymusic="/data/music/Van Halen/Van Halen - Right Now.mp3" + mymusic="/data/music/Van Halen/Van Halen - Right Now.mp3" - if [ -e "$mymusic" ]; then - echo "Let's rock" >&2 + if [ -e "$mymusic" ]; then + echo "Let's rock" >&2 else - echo "No music today, sorry..." >&2 + echo "No music today, sorry..." >&2 exit 1 fi @@ -75,7 +75,7 @@ filenames =\> **too many arguments!** **Another common mistake** is to provide too **few** arguments: - [ "$mystring"!="test" ] + [ "$mystring"!="test" ] This provides exactly **one** test-argument to the command. With one 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 might encounter: - var="" - if [ -n $var ]; then echo "var is not empty"; fi + var="" + if [ -n $var ]; then echo "var is not empty"; fi This code prints "var is not empty", even though `-n something` is supposed to be true if `$var` is not empty - **why?** @@ -228,8 +228,8 @@ them with the shell `&&` and `||` **list control operators**. See this: - if [ -n "$var"] && [ -e "$var"]; then - echo "\$var is not null and a file named $var exists!" + if [ -n "$var"] && [ -e "$var"]; then + echo "\$var is not null and a file named $var exists!" fi 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` and `-o`, thus: - if [ -n "$var" -a -e "$var" ] ; then - echo "\$var is not null and a file named $var exists" + if [ -n "$var" -a -e "$var" ] ; then + echo "\$var is not null and a file named $var exists" fi They are **not** `&&` or `||`: - $ if [ -n "/tmp" && -d "/tmp"]; then echo true; fi # DOES NOT WORK + $ if [ -n "/tmp" && -d "/tmp"]; then echo true; fi # DOES NOT WORK bash: [: missing `]' 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: - if [ -z "false" -a -z "$(echo I am executed >&2)" ] ; then ... + if [ -z "false" -a -z "$(echo I am executed >&2)" ] ; then ... =\> The arguments are all expanded **before** `test` runs, thus the echo-command **is executed**. - if [ -z "false" ] && [ -z "$(echo I am not executed >&2)" ]; then... + if [ -z "false" ] && [ -z "$(echo I am not executed >&2)" ]; then... =\> 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 @@ -305,10 +305,10 @@ the list way (`&&` and `||`): That means, **you can get different results**, depending on the manner of use: - $ if [ "true" ] || [ -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 - $ if [ "true" -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 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, and since `( ... )` introduces a subshell we will use `{ ... }` instead: - $ if [ "true" ] || { [ -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 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 interpret them: - $ if [ \( "true" -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 # equivalent, but less readable IMHO: - $ if [ '(' "true" -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 ## 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 (true), and the else part is executed: - if ! [ -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; 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 is also executed: - if [ ! -d '/tmp' ]; then echo "/tmp doesn't exists"; else echo "/tmp exists"; 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, at least for doing one single test. @@ -368,7 +368,7 @@ which you may have already had yourself**: Hi All, I've got a script that I'm trying to set up, but it keeps telling me - that "[-d command not found". Can someone please explain what is + that "[-d command not found". Can someone please explain what is wrong with this?: @@ -381,9 +381,9 @@ which you may have already had yourself**: { if [-d $i] then - echo "$i is a directory! Yay!" + echo "$i is a directory! Yay!" else - echo "$i is not a directory!" + echo "$i is not a directory!" fi } done @@ -410,7 +410,7 @@ test command: > (QUOTED TEXT WAS REMOVED) The shell is first and foremost a way to launch other commands. The - syntax is simply "if" 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 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 disappear entirely. This is why test arguments should always be quoted. - if test -d "$file" - if [ -d "$file" ] + if test -d "$file" + if [ -d "$file" ] Actually today test is defined that if only one argument is given as - in this case "test FOO" then then test returns true if the argument is - non-zero in text length. Because "-d" is non-zero length "test -d" is + in this case "test FOO" then then test returns true if the argument 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 avoids a case where depending upon the data may look like a test operator. - DATA="something" - if test "$DATA" # true, $DATA is non-zero length + DATA="something" + if test "$DATA" # true, $DATA is non-zero length - DATA="" - if test "$DATA" # false, $DATA is zero length + DATA="" + if test "$DATA" # false, $DATA is zero length 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 only one argument is defined to be the same as test -n $DATA. - DATA="-d" - if test "$DATA" # true, $DATA is non-zero length + DATA="-d" + if test "$DATA" # true, $DATA is non-zero length if test -d # true, same as previous case. 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: - if test 5 > 2 # true, "5" is non-zero length, creates file named "2" + if test 5 > 2 # true, "5" is non-zero length, creates file named "2" Intended use: @@ -556,7 +556,7 @@ entries of a directory, if an entry is a directory (`[ -d "$fn" ]`), print its name: for fn in *; do - [ -d "$fn" ] && echo "$fn" + [ -d "$fn" ] && echo "$fn" done ## See also diff --git a/howto/calculate-dc.md b/howto/calculate-dc.md index 56ea36d..ace67ec 100644 --- a/howto/calculate-dc.md +++ b/howto/calculate-dc.md @@ -63,7 +63,7 @@ The **output** is converted to **base 10** by default ## Scale And Base `dc` is a calulator with abitrary precision, by default this precision -is 0. thus `dc <<< "5 4/p"` prints "1". +is 0. thus `dc <<< "5 4/p"` prints "1". 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: @@ -198,7 +198,7 @@ times, or use a macro. [dd**] # push a string sa # save it in register a 3 # push 3 on the stack - lax # push the string "dd**" on the stack and execute it + lax # push the string "dd**" on the stack and execute it p # print the result 4laxp # same operation for 4, in one line EOF @@ -221,7 +221,7 @@ we are used to reading: Some `dc` have `>R R 1 2 >R f"` 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 as `dc` relies on a stack we can, in fact, use the macro recursively diff --git a/howto/collapsing_functions.md b/howto/collapsing_functions.md index 11f9a84..059a25d 100644 --- a/howto/collapsing_functions.md +++ b/howto/collapsing_functions.md @@ -22,9 +22,9 @@ common example is a script that gives the user the option of having chatter() { if [[ $verbose ]]; then chatter() { - echo "$@" + echo "$@" } - chatter "$@" + chatter "$@" else chatter() { : @@ -32,9 +32,9 @@ common example is a script that gives the user the option of having fi } - echo "Waiting for 10 seconds." + echo "Waiting for 10 seconds." for i in {1..10}; do - chatter "$i" + chatter "$i" sleep 1 done @@ -56,35 +56,35 @@ FIXME Add more examples! # various versions of -perm /+ blah blah and hacks find() { hash find || { echo 'find not found!'; exit 1; } - # We can be pretty sure "$0" should be executable. - if [[ $(command find "$0" -executable 2> /dev/null) ]]; then + # We can be pretty sure "$0" should be executable. + if [[ $(command find "$0" -executable 2> /dev/null) ]]; then unset -f find # We can just use the command find - elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then + elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then find() { typeset arg args for arg do - [[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg") + [[ $arg = -executable ]] && args+=(-perm /u+x) || args+=("$arg") done - command find "${args[@]}" + command find "${args[@]}" } - elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then + elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then find() { typeset arg args for arg do - [[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg") + [[ $arg = -executable ]] && args+=(-perm +u+x) || args+=("$arg") done - command find "${args[@]}" + command find "${args[@]}" } else # Last resort find() { typeset arg args for arg do - [[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg") + [[ $arg = -executable ]] && args+=(-exec test -x {} \; -print) || args+=("$arg") done - command find "${args[@]}" + command find "${args[@]}" } fi - find "$@" + find "$@" } \ \#!/bin/bash \# Using collapsing functions to turn debug diff --git a/howto/conffile.md b/howto/conffile.md index 934f331..c092689 100644 --- a/howto/conffile.md +++ b/howto/conffile.md @@ -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: #!/bin/bash - echo "Reading config...." >&2 + echo "Reading config...." >&2 source /etc/cool.cfg - echo "Config for the username: $cool_username" >&2 - echo "Config for the target host: $cool_host" >&2 + echo "Config for the username: $cool_username" >&2 + echo "Config for the target host: $cool_host" >&2 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 @@ -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 like: - cool_username="guest" - cool_host="foo.example.com" + cool_username="guest" + cool_host="foo.example.com" These are normal statements understood by Bash, nothing special. Of 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: #!/bin/bash - echo "Reading config...." >&2 + echo "Reading config...." >&2 . /etc/cool.cfg #note the space between the dot and the leading slash of /etc.cfg - echo "Config for the username: $cool_username" >&2 - echo "Config for the target host: $cool_host" >&2 + echo "Config for the username: $cool_username" >&2 + echo "Config for the target host: $cool_host" >&2 ## 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: #!/bin/bash - echo "Reading system-wide config...." >&2 + echo "Reading system-wide config...." >&2 . /etc/cool.cfg if [ -r ~/.coolrc ]; then - echo "Reading user config...." >&2 + echo "Reading user config...." >&2 . ~/.coolrc fi @@ -68,9 +68,9 @@ malicious code: username=god_only_knows hostname=www.example.com password=secret ; echo rm -rf ~/* - parameter=foobar && echo "You've bene pwned!"; + parameter=foobar && echo "You've bene pwned!"; # hey look, weird code follows... - echo "I am the skull virus..." + echo "I am the skull virus..." echo rm -fr ~/* mailto=netadmin@example.com @@ -88,15 +88,15 @@ filters by description: configfile_secured='/tmp/cool.cfg' # check if the file contains something we don't want - if egrep -q -v '^#|^[^ ]*=[^;]*' "$configfile"; then - echo "Config file is unclean, cleaning it..." >&2 + if egrep -q -v '^#|^[^ ]*=[^;]*' "$configfile"; then + echo "Config file is unclean, cleaning it..." >&2 # filter the original to a new file - egrep '^#|^[^ ]*=[^;&]*' "$configfile" > "$configfile_secured" - configfile="$configfile_secured" + egrep '^#|^[^ ]*=[^;&]*' "$configfile" > "$configfile_secured" + configfile="$configfile_secured" fi # now source it, either the original or the filtered variant - source "$configfile" + source "$configfile" **To make clear what it does:** egrep checks if the file contains something we don't want, if yes, egrep filters the file and writes the diff --git a/howto/dissectabadoneliner.md b/howto/dissectabadoneliner.md index 17ac6a8..fbb7c00 100644 --- a/howto/dissectabadoneliner.md +++ b/howto/dissectabadoneliner.md @@ -41,7 +41,7 @@ let's use the more readable, [modern](/syntax/expansion/cmdsubst) `$()` construct instead of the old style backticks: ``` bash -sh $ for i in *.zip; do j=$(basename "$i" ".zip"); 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 @@ -49,7 +49,7 @@ In Bash we don't need the subshell or the external basename command. See expansion](/syntax/pe#substring_removal): ``` bash -bash $ for i in *.zip; do j="${i%.zip}"; 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: @@ -69,7 +69,7 @@ in the previous step? Well, if you don't quote `$j`, wordsplitting can happen again. ``` bash -$ mkdir "$j" && cd "$j" && ... && cd .. +$ mkdir "$j" && cd "$j" && ... && cd .. ``` 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: ``` bash -$ mkdir "$j" && cd "$j" && ... && cd - +$ mkdir "$j" && cd "$j" && ... && cd - ``` (If it occurred to you that I forgot to check for success after cd -, @@ -90,17 +90,17 @@ problem.) So now we have: ``` bash -sh $ for i in *.zip; do j=$(basename "$i" ".zip"); 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 ``` ``` bash -bash $ for i in *.zip; do j="${i%.zip}"; 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 throw the `unzip` command back in the mix: ``` bash -mkdir "$j" && cd "$j" && unzip ../$i && cd - +mkdir "$j" && cd "$j" && unzip ../$i && cd - ``` 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: ``` bash -$ mkdir "$j" && unzip -d "$j" "$i" +$ mkdir "$j" && unzip -d "$j" "$i" ``` ``` bash -sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && unzip -d "$j" "$i"; done +sh $ for i in *.zip; do j=$(basename "$i" ".zip"); mkdir "$j" && unzip -d "$j" "$i"; done ``` ``` bash -bash $ for i in *.zip; do j="${i%.zip}"; mkdir "$j" && unzip -d "$j" "$i"; 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. diff --git a/howto/edit-ed.md b/howto/edit-ed.md index a74a6e6..d96a77a 100644 --- a/howto/edit-ed.md +++ b/howto/edit-ed.md @@ -38,10 +38,10 @@ command, `printf` ("help printf"). Shown here as an example Bash function to prefix text to file content: - # insertHead "$text" "$file" + # insertHead "$text" "$file" insertHead() { - printf '%s\n' H 1i "$1" . w | ed -s "$2" + printf '%s\n' H 1i "$1" . w | ed -s "$2" } **Here-strings** @@ -221,9 +221,9 @@ prints the result to stdout - `,p`): To compare, here's a possible `sed` solution which must use Bash arithmetic and the external program `wc`: - sed "$(($(wc -l < FILE1)-1))r FILE2" FILE1 + sed "$(($(wc -l < FILE1)-1))r FILE2" FILE1 - # UPDATE here's one which uses GNU sed's "e" 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 # security risk, but well, sometimes GNU > security, you know... sed '${h;s/.*/cat FILE2/e;G}' FILE1 @@ -250,10 +250,10 @@ about it with the g (global) command: echo $'1\n1\n3' > file - #replace all lines matching 1 by "replacement" + #replace all lines matching 1 by "replacement" ed -s file <<< $'g/1/s/1/replacement/\n,p' - #replace the first line matching 1 by "replacement" + #replace the first line matching 1 by "replacement" #(because it starts searching from the last line) 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 succeed and ed will not produce an error when foo is not found: - #Second version will add the line with "something" 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' In fact, even a substitution that fails after a g/ / command does not diff --git a/howto/getopts_tutorial.md b/howto/getopts_tutorial.md index 9a466ce..63afd13 100644 --- a/howto/getopts_tutorial.md +++ b/howto/getopts_tutorial.md @@ -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 example from an array: - while getopts :f:h opt "${MY_OWN_SET[@]}"; do + while getopts :f:h opt "${MY_OWN_SET[@]}"; do ... done A call to `getopts` **without** these additional arguments is **equivalent** to explicitly calling it with `"$@"`: - getopts ... "$@" + getopts ... "$@" ### Error Reporting @@ -189,13 +189,13 @@ preceding the whole option string with a colon (`:`): ``` bash #!/bin/bash -while getopts ":a" opt; do +while getopts ":a" opt; do case $opt in a) - echo "-a was triggered!" >&2 + echo "-a was triggered!" >&2 ;; \?) - echo "Invalid option: -$OPTARG" >&2 + echo "Invalid option: -$OPTARG" >&2 ;; esac done @@ -285,17 +285,17 @@ Let's extend our example from above. Just a little bit: ``` bash #!/bin/bash -while getopts ":a:" opt; do +while getopts ":a:" opt; do case $opt in a) - echo "-a was triggered, Parameter: $OPTARG" >&2 + echo "-a was triggered, Parameter: $OPTARG" >&2 ;; \?) - echo "Invalid option: -$OPTARG" >&2 + echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) - echo "Option -$OPTARG requires an argument." >&2 + echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac diff --git a/howto/mutex.md b/howto/mutex.md index 69585fb..128f144 100644 --- a/howto/mutex.md +++ b/howto/mutex.md @@ -67,9 +67,9 @@ With mkdir it seems, we have our two steps in one simple operation. A ``` bash if mkdir /var/lock/mylock; then - echo "Locking succeeded" >&2 + echo "Locking succeeded" >&2 else - echo "Lock failed - exit" >&2 + echo "Lock failed - exit" >&2 exit 1 fi ``` @@ -103,12 +103,12 @@ Need to write a code example here. ``` bash -if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then - trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT - echo "Locking succeeded" >&2 - rm -f "$lockfile" +if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then + trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT + echo "Locking succeeded" >&2 + rm -f "$lockfile" else - echo "Lock failed - exit" >&2 + echo "Lock failed - exit" >&2 exit 1 fi @@ -136,59 +136,59 @@ the locking process is shown: #!/bin/bash # lock dirs/files -LOCKDIR="/tmp/statsgen-lock" -PIDFILE="${LOCKDIR}/PID" +LOCKDIR="/tmp/statsgen-lock" +PIDFILE="${LOCKDIR}/PID" # exit codes and text -ENO_SUCCESS=0; ETXT[0]="ENO_SUCCESS" -ENO_GENERAL=1; ETXT[1]="ENO_GENERAL" -ENO_LOCKFAIL=2; ETXT[2]="ENO_LOCKFAIL" -ENO_RECVSIG=3; ETXT[3]="ENO_RECVSIG" +ENO_SUCCESS=0; ETXT[0]="ENO_SUCCESS" +ENO_GENERAL=1; ETXT[1]="ENO_GENERAL" +ENO_LOCKFAIL=2; ETXT[2]="ENO_LOCKFAIL" +ENO_RECVSIG=3; ETXT[3]="ENO_RECVSIG" ### ### start locking attempt ### -trap 'ECODE=$?; echo "[statsgen] Exit: ${ETXT[ECODE]}($ECODE)" >&2' 0 -echo -n "[statsgen] Locking: " >&2 +trap 'ECODE=$?; echo "[statsgen] Exit: ${ETXT[ECODE]}($ECODE)" >&2' 0 +echo -n "[statsgen] Locking: " >&2 -if mkdir "${LOCKDIR}" &>/dev/null; then +if mkdir "${LOCKDIR}" &>/dev/null; then # lock succeeded, install signal handlers before storing the PID just in case # storing the PID fails trap 'ECODE=$?; - echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2 - rm -rf "${LOCKDIR}"' 0 - echo "$$" >"${PIDFILE}" + echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2 + rm -rf "${LOCKDIR}"' 0 + echo "$$" >"${PIDFILE}" # the following handler will exit the script upon receiving these signals - # the trap on "0" (EXIT) from above will be triggered by this trap's "exit" command! - trap 'echo "[statsgen] Killed by a signal." >&2 + # the trap on "0" (EXIT) from above will be triggered by this trap's "exit" command! + trap 'echo "[statsgen] Killed by a signal." >&2 exit ${ENO_RECVSIG}' 1 2 3 15 - echo "success, installed signal handlers" + echo "success, installed signal handlers" else # lock failed, check if the other PID is alive - OTHERPID="$(cat "${PIDFILE}")" + OTHERPID="$(cat "${PIDFILE}")" # if cat isn't able to read the file, another instance is probably # about to remove the lock -- exit, we're *still* locked # Thanks to Grzegorz Wierzowiecki for pointing out this race condition on # http://wiki.grzegorz.wierzowiecki.pl/code:mutex-in-bash if [ $? != 0 ]; then - echo "lock failed, PID ${OTHERPID} is active" >&2 + echo "lock failed, PID ${OTHERPID} is active" >&2 exit ${ENO_LOCKFAIL} fi if ! kill -0 $OTHERPID &>/dev/null; then # lock is stale, remove it and restart - echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2 - rm -rf "${LOCKDIR}" - echo "[statsgen] restarting myself" >&2 - exec "$0" "$@" + echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2 + rm -rf "${LOCKDIR}" + echo "[statsgen] restarting myself" >&2 + exec "$0" "$@" else # lock is valid and OTHERPID is active - exit, we're locked! - echo "lock failed, PID ${OTHERPID} is active" >&2 + echo "lock failed, PID ${OTHERPID} is active" >&2 exit ${ENO_LOCKFAIL} fi diff --git a/howto/pax.md b/howto/pax.md index d8a899f..0fcf410 100644 --- a/howto/pax.md +++ b/howto/pax.md @@ -183,7 +183,7 @@ give the `-v` option: You can extract all files, or files (not) matching specific patterns from an archive using constructs like: - # "normal" extraction + # "normal" extraction pax -rf myarchive.tar '*.txt' # with inverted pattern @@ -201,8 +201,8 @@ command, use: To copy directory contents to another directory on a remote system, use: - pax -w localdir | ssh user@host "cd distantdest && pax -r -v" - pax -w localdir | gzip | ssh user@host "cd distantdir && gunzip | pax -r -v" #compress the sent data + pax -w localdir | ssh user@host "cd distantdest && pax -r -v" + 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) on the remote machine. diff --git a/howto/redirection_tutorial.md b/howto/redirection_tutorial.md index 3f47eb8..7fee310 100644 --- a/howto/redirection_tutorial.md +++ b/howto/redirection_tutorial.md @@ -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 easy, we just do: - while read -r line;do echo "$line";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 user to press a key: - while read -r line;do echo "$line"; read -p "Press any key" -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 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: exec 3someFile - # 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). + # 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. cmd 2>& 1 <<< stuff diff --git a/howto/testing-your-scripts.md b/howto/testing-your-scripts.md index eb505ee..00a4774 100644 --- a/howto/testing-your-scripts.md +++ b/howto/testing-your-scripts.md @@ -9,18 +9,18 @@ We have a simple **stat.sh** script: #!/usr/bin/env bash - if [ -z "$1" ] + if [ -z "$1" ] then DIR=./ else DIR=$1 fi - echo "Evaluate *.py statistics" + echo "Evaluate *.py statistics" FILES=$(find $DIR -name '*.py' | wc -l) LINES=$((find $DIR -name '*.py' -print0 | xargs -0 cat) | wc -l) - echo "PYTHON FILES: $FILES" - echo "PYTHON LINES: $LINES" + echo "PYTHON FILES: $FILES" + echo "PYTHON LINES: $LINES" 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 \** diff --git a/misc/readthesourceluke.md b/misc/readthesourceluke.md index fdac977..f427cda 100644 --- a/misc/readthesourceluke.md +++ b/misc/readthesourceluke.md @@ -17,7 +17,7 @@ From `builtins/read.def`: ``` C /* If there are no variables, save the text of the line read to the variable $REPLY. ksh93 strips leading and trailing IFS whitespace, - so that `read x ; echo "$x"' and `read ; echo "$REPLY"' 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 enough to not do it. Without the bash behavior, there is no way to read a line completely without interpretation or modification diff --git a/scripting/bashchanges.md b/scripting/bashchanges.md index 9aba355..21f616f 100644 --- a/scripting/bashchanges.md +++ b/scripting/bashchanges.md @@ -249,7 +249,7 @@ For this topic, see also | `${PARAMETER//PATTERN/REPLACEMENT}` | 2.0 | | | `${PARAMETER:OFFSET:LENGTH}` | 2.0 | | | `${!PARAMETER}` (indirection) | 2.0 | | -| `$"..."` (localized strings) | 2.0 | | +| `$"..."` (localized strings) | 2.0 | | | `$'...'` (ANSI-C-like strings) | 2.0 | | | `\xNNN` in `$'...'` (and `echo -e`) | 2.02-alpha1 | | | `$(< FILENAME)` (file content) | 2.02-alpha1 | | diff --git a/scripting/basics.md b/scripting/basics.md index 92e2c5d..e3bbb2e 100644 --- a/scripting/basics.md +++ b/scripting/basics.md @@ -27,7 +27,7 @@ The in-file specification of the interpreter of that file, for example: ``` bash #!/bin/bash -echo "Hello world..." +echo "Hello world..." ``` 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 if grep ^root /etc/passwd; then - echo "The user root was found" + echo "The user root was found" else - echo "The user root was not found" + echo "The user root was not found" fi ``` @@ -176,10 +176,10 @@ brackets are not part of the shell syntax, the left bracket **is** the test command! ``` bash -if [ "$mystring" = "Hello world" ]; then - echo "Yeah dude, you entered the right words..." +if [ "$mystring" = "Hello world" ]; then + echo "Yeah dude, you entered the right words..." else - echo "Eeeek - go away..." + echo "Eeeek - go away..." fi ``` @@ -190,8 +190,8 @@ This lets you execute a command based on whether or not the previous command completed successfully: ``` bash -grep ^root: /etc/passwd >/dev/null || echo "root was not found - check the pub at the corner." -which vi && echo "Your favourite editor is installed." +grep ^root: /etc/passwd >/dev/null || echo "root was not found - check the pub at the corner." +which vi && echo "Your favourite editor is installed." ``` 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 #!/bin/bash # This is a small script to say something. -echo "Be liberal in what you accept, and conservative in what you send" # 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 @@ -227,14 +227,14 @@ a shutdown: ``` bash #!/bin/bash # Write info mails, do some tasks and bring down the system in a safe way -echo "System halt requested" | mail -s "System halt" netadmin@example.com -logger -t SYSHALT "System halt requested" +echo "System halt requested" | mail -s "System halt" netadmin@example.com +logger -t SYSHALT "System halt requested" -##### The following "code block" is effectively ignored -: <<"SOMEWORD" +##### The following "code block" is effectively ignored +: <<"SOMEWORD" /etc/init.d/mydatabase clean_stop mydatabase_dump /var/db/db1 /mnt/fsrv0/backups/db1 -logger -t SYSHALT "System halt: pre-shutdown actions done, now shutting down the system" +logger -t SYSHALT "System halt: pre-shutdown actions done, now shutting down the system" shutdown -h NOW SOMEWORD ##### The ignored codeblock ends here @@ -312,13 +312,13 @@ echo $foo } -# this will print "external" +# this will print "external" echo $foo -# this will print "internal" +# this will print "internal" printvalue -# this will print - again - "external" +# this will print - again - "external" echo $foo ``` @@ -341,10 +341,10 @@ A variable can be tagged to be part of the environment using the ``` bash # create a new variable and set it: # -> This is a normal shell variable, not an environment variable! -myvariable="Hello world." +myvariable="Hello world." # make the variable visible to all child processes: -# -> Make it an environment variable: "export" it +# -> Make it an environment variable: "export" it export myvariable ``` diff --git a/scripting/debuggingtips.md b/scripting/debuggingtips.md index b943d42..522070e 100644 --- a/scripting/debuggingtips.md +++ b/scripting/debuggingtips.md @@ -52,20 +52,20 @@ manpage](http://unixhelp.ed.ac.uk/CGI/man-cgi?logger+1)). Insert **echos** everywhere you can, and print to `stderr`: - echo "DEBUG: current i=$i" >&2 + echo "DEBUG: current i=$i" >&2 If you read input from **anywhere**, such as a file or [command substitution](/syntax/expansion/cmdsubst), print the debug output with literal quotes, to see leading and trailing spaces! pid=$(< fooservice.pid) - echo "DEBUG: read from file: pid=\"$pid\"" >&2 + echo "DEBUG: read from file: pid=\"$pid\"" >&2 Bash's [printf](/commands/builtin/printf) command has the `%q` format, which is handy for verifying whether strings are what they appear to be. foo=$(< inputfile) - printf "DEBUG: foo is |%q|\n" "$foo" >&2 + printf "DEBUG: foo is |%q|\n" "$foo" >&2 # exposes whitespace (such as CRs, see below) and non-printing characters ## 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: set -x - foo="bar baz" + foo="bar baz" [ $foo = test ] 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... # next try - [ "$foo" = test ] + [ "$foo" = test ] `xtrace` now gives @@ -157,10 +157,10 @@ For general debugging purposes you can also define a function and a variable to use: debugme() { - [[ $script_debug = 1 ]] && "$@" || : + [[ $script_debug = 1 ]] && "$@" || : # 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 - # "false" 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) } @@ -171,9 +171,9 @@ Use it like this: script_debug=1 # to turn it off, set script_debug=0 - debugme logger "Sorting the database" + debugme logger "Sorting the database" database_sort - debugme logger "Finished sorting the database, exit code $?" + debugme logger "Finished sorting the database, exit code $?" Of course this can be used to execute something other than echo during 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 ... - script.sh: line 50: unexpected EOF while looking for matching `"' + script.sh: line 50: unexpected EOF while looking for matching `"' script.sh: line 100: syntax error: unexpected end of file 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 - $ echo "Hello world!" - bash: !": event not found + $ echo "Hello world!" + bash: !": event not found This is not an error per se. It happens in interactive shells, when the 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 commandline, e.g. - $ foo () { echo "Hello world"; } + $ foo () { echo "Hello world"; } bash: syntax error near unexpected token `(' you most likely have an alias defined with the same name as the function @@ -323,7 +323,7 @@ carriage return character!): #!/bin/bash^M ^M - echo "Hello world"^M + echo "Hello world"^M ... Here's what happens because of the `#!/bin/bash^M` in our shebang: diff --git a/scripting/newbie_traps.md b/scripting/newbie_traps.md index 702bccf..7f66a8a 100644 --- a/scripting/newbie_traps.md +++ b/scripting/newbie_traps.md @@ -85,13 +85,13 @@ There is no `$` (dollar-sign) when you reference the **name** of a variable! Bash is not PHP! # THIS IS WRONG! - $myvar="Hello world!" + $myvar="Hello world!" A variable name preceeded with a dollar-sign always means that the variable gets **expanded**. In the example above, it might expand to nothing (because it wasn't set), effectively resulting in... - ="Hello world!" + ="Hello world!" ...which **definitely is wrong**! @@ -129,7 +129,7 @@ assigned value**: example=Hello # CORRECT 2 - example=" Hello" + example=" Hello" ### Expanding (using) variables @@ -142,7 +142,7 @@ result isn't always the same. Let's define an example variable containing text with spaces: - example="Hello world" + example="Hello world" | 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`): # WRONG! - echo "The first character of PATH is ${$PATH:0:1}" + echo "The first character of PATH is ${$PATH:0:1}" # CORRECT - echo "The first character of PATH is ${PATH:0:1}" + echo "The first character of PATH is ${PATH:0:1}" Note that if you are using variables in [arithmetic 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 if [ $? -ne 0 ]; then - echo "root was not found - check the pub at the corner" + echo "root was not found - check the pub at the corner" fi ``` @@ -225,14 +225,14 @@ This can be simplified to: ``` bash if ! grep ^root: /etc/passwd >/dev/null 2>&1; then - echo "root was not found - check the pub at the corner" + echo "root was not found - check the pub at the corner" fi ``` Or, simpler yet: ``` bash -grep ^root: /etc/passwd >/dev/null 2>&1 || echo "root was not found - check the pub at the corner" +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 @@ -252,7 +252,7 @@ When you want to run a command (or a pipeline) and save (or print) the syntax: $(ls -l /tmp) - newvariable=$(printf "foo") + newvariable=$(printf "foo") 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: diff --git a/scripting/nonportable.md b/scripting/nonportable.md index 52d1cee..d827ab5 100644 --- a/scripting/nonportable.md +++ b/scripting/nonportable.md @@ -134,7 +134,7 @@ exit code, a 100% equivalent construct would be: ... # portable equivalent command - if [ "$((MATH))" -ne 0 ]; then + if [ "$((MATH))" -ne 0 ]; then ... 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. # '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["pid"]); 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) - 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)} }') + 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 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`: if hash ls >/dev/null 2>&1; then - echo "ls is available" + echo "ls is available" fi Somewhat of a mass-check: for name in ls grep sed awk; do - if ! hash "$name" >/dev/null 2>&1; then - echo "FAIL: Missing command '$name'" + if ! hash "$name" >/dev/null 2>&1; then + echo "FAIL: Missing command '$name'" exit 1 fi done @@ -257,7 +257,7 @@ For example, to check if the command `sed` is available in a location accessible by `PATH`: if command -v sed >/dev/null 2>&1; then - echo "sed is available" + echo "sed is available" fi [^1]: "portable" doesn't necessarily mean it's POSIX, it can also mean diff --git a/scripting/posparams.md b/scripting/posparams.md index a860a24..a20197e 100644 --- a/scripting/posparams.md +++ b/scripting/posparams.md @@ -35,7 +35,7 @@ shell initialization: Testscript - it just echos `$0`: #!/bin/bash - echo "$0" + echo "$0" You see, `$0` is always set to the name the script is called with (`>` is the prompt...): @@ -48,7 +48,7 @@ is the prompt...): However, this isn't true for login shells: - > echo "$0" + > echo "$0" -bash 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: #!/bin/bash - echo "Total number of arguments: $#" - echo "Argument 1: $1" - echo "Argument 2: $2" - echo "Argument 3: $3" - echo "Argument 4: $4" - echo "Argument 5: $5" + echo "Total number of arguments: $#" + echo "Argument 1: $1" + echo "Argument 2: $2" + echo "Argument 3: $3" + echo "Argument 4: $4" + echo "Argument 5: $5" 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 @@ -111,7 +111,7 @@ argument list: numargs=$# for ((i=1 ; i <= numargs ; i++)) do - echo "$1" + echo "$1" shift done @@ -126,7 +126,7 @@ a given wordlist. The loop uses the positional parameters as a wordlist: for arg do - echo "$arg" + echo "$arg" done Advantage: 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 expands to something, using the [test command](/commands/classictest): - while [ "$1" ] + while [ "$1" ] do - echo "$1" + echo "$1" shift 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 value](/syntax/pe#use_an_alternate_value): - while [ "${1+defined}" ]; do - echo "$1" + while [ "${1+defined}" ]; do + echo "$1" shift done @@ -226,7 +226,7 @@ decremented. **Example:** START at the last positional parameter: - echo "${@: -1}" + echo "${@: -1}" **Attention**: As of Bash 4, a `START` of `0` includes the 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 inside the script or function: - set "This is" my new "set of" positional parameters + set "This is" my new "set of" positional parameters # RESULTS IN # $1: This is @@ -282,9 +282,9 @@ rudimentary way to parse your arguments. while : do - case "$1" in + case "$1" in -f | --file) - file="$2" # You may want to check validity of $2 + file="$2" # You may want to check validity of $2 shift 2 ;; -h | --help) @@ -293,31 +293,31 @@ rudimentary way to parse your arguments. exit 0 ;; -u | --user) - username="$2" # You may want to check validity of $2 + username="$2" # You may want to check validity of $2 shift 2 ;; -v | --verbose) - # It's better to assign a string, than a number like "verbose=1" - # because if you're debugging the script with "bash -x" code like this: + # It's better to assign a string, than a number like "verbose=1" + # because if you're debugging the script with "bash -x" code like this: # - # if [ "$verbose" ] ... + # if [ "$verbose" ] ... # # You will see: # - # if [ "verbose" ] ... + # if [ "verbose" ] ... # # Instead of cryptic # - # if [ "1" ] ... + # if [ "1" ] ... # - verbose="verbose" + verbose="verbose" shift ;; --) # End of all options shift break; -*) - echo "Error: Unknown option: $1" >&2 + echo "Error: Unknown option: $1" >&2 exit 1 ;; *) # No more options @@ -346,7 +346,7 @@ options" for `ls` and doesn't change anything after it: while [[ $1 ]] do if ! ((eoo)); then - case "$1" in + case "$1" in -a) shift ;; @@ -354,29 +354,29 @@ options" for `ls` and doesn't change anything after it: shift ;; -[^-]*a*|-a?*) - options+=("${1//a}") + options+=("${1//a}") shift ;; --) eoo=1 - options+=("$1") + options+=("$1") shift ;; *) - options+=("$1") + options+=("$1") shift ;; esac else - options+=("$1") + options+=("$1") # Another (worse) way of doing the same thing: - # options=("${options[@]}" "$1") + # options=("${options[@]}" "$1") shift fi done - /bin/ls "${options[@]}" + /bin/ls "${options[@]}" ### Using getopts diff --git a/scripting/processtree.md b/scripting/processtree.md index 6fe0c3d..d95b715 100644 --- a/scripting/processtree.md +++ b/scripting/processtree.md @@ -96,7 +96,7 @@ into a variable. We run it in a loop here to count input lines: counter=0 cat /etc/passwd | while read; do ((counter++)); done - echo "Lines: $counter" + echo "Lines: $counter" 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 @@ -119,7 +119,7 @@ counter must be the "main shell". For example: counter=0 while read; do ((counter++)); done /dev/null 2>&1; then - printf "Command not found in PATH: %s\n" "$needed_command" >&2 + if ! hash "$needed_command" >/dev/null 2>&1; then + printf "Command not found in PATH: %s\n" "$needed_command" >&2 ((missing_counter++)) fi done if ((missing_counter > 0)); then - printf "Minimum %d commands are missing in PATH, aborting\n" "$missing_counter" >&2 + printf "Minimum %d commands are missing in PATH, aborting\n" "$missing_counter" >&2 exit 1 fi diff --git a/scripting/terminalcodes.md b/scripting/terminalcodes.md index 43e6eb2..193585f 100644 --- a/scripting/terminalcodes.md +++ b/scripting/terminalcodes.md @@ -218,9 +218,9 @@ done by the following commands: tput smcup clear - # example "application" follows... - read -n1 -p "Press any key to continue..." - # example "application" ends here + # example "application" follows... + read -n1 -p "Press any key to continue..." + # example "application" ends here # restore tput rmcup @@ -281,14 +281,14 @@ switch to, to get the other 8 colors. Directly inside the echo: - echo "TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database." + echo "TPUT is a $(tput setaf 2)nice$(tput setaf 9) and $(tput setaf 5)user friendly$(tput setaf 9) terminal capability database." With preset variables: - COL_NORM="$(tput setaf 9)" - COL_RED="$(tput setaf 1)" - COL_GREEN="$(tput setaf 2)" - echo "It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?" + COL_NORM="$(tput setaf 9)" + COL_RED="$(tput setaf 1)" + COL_GREEN="$(tput setaf 2)" + echo "It's ${COL_RED}red${COL_NORM} and ${COL_GREEN}green${COL_NORM} - have you seen?" ### Misc @@ -303,11 +303,11 @@ switch to, to get the other 8 colors. #!/bin/bash - DATA[0]=" _/ _/ _/ _/ " - DATA[1]=" _/_/_/_/_/ _/_/_/ _/_/_/ _/_/_/ _/_/_/ " - DATA[2]=" _/ _/ _/ _/ _/ _/ _/_/ _/ _/" - DATA[3]="_/_/_/_/_/ _/ _/ _/ _/ _/_/ _/ _/ " - DATA[4]=" _/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ " + DATA[0]=" _/ _/ _/ _/ " + DATA[1]=" _/_/_/_/_/ _/_/_/ _/_/_/ _/_/_/ _/_/_/ " + DATA[2]=" _/ _/ _/ _/ _/ _/ _/_/ _/ _/" + DATA[3]="_/_/_/_/_/ _/ _/ _/ _/ _/_/ _/ _/ " + DATA[4]=" _/ _/ _/_/_/ _/_/_/ _/_/_/ _/ _/ " # 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 { - (($1==lastclr)) || printf %s "${colrs[lastclr=$1]:=$(tput setaf "$1")}" + (($1==lastclr)) || printf %s "${colrs[lastclr=$1]:=$(tput setaf "$1")}" printf '\u2588' } diff --git a/syntax/arith_expr.md b/syntax/arith_expr.md index 3b05dff..58bf030 100644 --- a/syntax/arith_expr.md +++ b/syntax/arith_expr.md @@ -48,7 +48,7 @@ as a decimal value, you need to expand the parameter and specify base # this is interpreted as an octal: echo $(( x )) - # this is an invalid digit for base 10 (the "x")...: + # this is an invalid digit for base 10 (the "x")...: echo $(( 10#x )) ## Different bases @@ -88,7 +88,7 @@ mean: 42 $ echo $((43#H)) - bash: 43#H: value too great for base (error token is "43#H") + 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, and what numbers are and how they are built, then you don't need @@ -111,7 +111,7 @@ named parameters, e.g.: string=3 echo $((test)) - # will output "3"! + # will output "3"! Of course, in the end, when it finally evaluates to something that is **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: if ((0)); then - echo "true" + echo "true" else - echo "false" + echo "false" fi ## Operators @@ -318,9 +318,9 @@ and all the others, including `set -e` for autoexit on error: MY_TEST_FLAG=0 if ((MY_TEST_FLAG)); then - echo "MY_TEST_FLAG is ON" + echo "MY_TEST_FLAG is ON" else - echo "MY_TEST_FLAG is OFF" + echo "MY_TEST_FLAG is OFF" fi ``` diff --git a/syntax/arrays.md b/syntax/arrays.md index 72ad9eb..b25093d 100644 --- a/syntax/arrays.md +++ b/syntax/arrays.md @@ -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 synonymous with referring to the array name without a subscript. - # "x" is an ordinary non-array parameter. - $ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}" + # "x" is an ordinary non-array parameter. + $ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}" hi hi hi 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)) for ((i = 0; i <= max_index; i++)); do - echo "Element $i: '${sentence[i]}'" + echo "Element $i: '${sentence[i]}'" done You always have to remember that, it seems newbies have problems @@ -291,7 +291,7 @@ think you could just do $ echo ${#sentence[@]} 1 # omit calculating max_index as above, and iterate as one-liner - $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done + $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done Element 0: 'NAMES' Obviously that's wrong. What about @@ -302,16 +302,16 @@ Obviously that's wrong. What about $ echo ${#sentence[*]} 1 - $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done + $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done Element 0: 'Peter' So what's the **right** way? The (slightly ugly) answer is, reuse the enumeration syntax: - $ unset sentence ; declare -a sentence=("${NAMES[@]}") + $ unset sentence ; declare -a sentence=("${NAMES[@]}") $ echo ${#sentence[@]} 4 - $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done + $ for ((i = 0; i < ${#sentence[@]}; i++)); do echo "Element $i: '${sentence[i]}'" ; done Element 0: 'Peter' Element 1: 'Anna' 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: # output from 'set' command - sentence=([End]="in what you send" [Middle]="you accept, and conservative " [Begin]="Be liberal in what " ["Very end"]="...") + 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 `"${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 usually query known index values: - for element in Begin Middle End "Very end"; do - printf "%s" "${sentence[$element]}" + for element in Begin Middle End "Very end"; do + printf "%s" "${sentence[$element]}" done - printf "\n" + printf "\n" **A nice code example:** Checking for duplicate files using an 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; while read -r sum fname; do if [[ ${flist[$sum]} ]]; then - printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}" + printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}" else - flist[$sum]="$fname" + flist[$sum]="$fname" fi 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 for ordinary integer variables. - ~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]="a[2]")' 'a+=(42 [a[4]]+=3)'; declare -p a ) - declare -ai a='([0]="6" [2]="4" [4]="7" [5]="42")' + ~ $ ( declare -ia 'a=(2+4 [2]=2+2 [a[2]]="a[2]")' 'a+=(42 [a[4]]+=3)'; declare -p a ) + 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 `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. isSubset() { - local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")' - set -- "${@/%/[key]}" + local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")' + set -- "${@/%/[key]}" (( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1 local key - for key in "${xkeys[@]}"; do + for key in "${xkeys[@]}"; do [[ ${!2+_} && ${!1} == ${!2} ]] || return 1 done } main() { - # "a" is a subset of "b" + # "a" is a subset of "b" local -a 'a=({0..5})' 'b=({0..10})' isSubset a b echo $? # true - # "a" contains a key not in "b" + # "a" contains a key not in "b" local -a 'a=([5]=5 {6..11})' 'b=({0..10})' isSubset a b echo $? # false - # "a" contains an element whose value != the corresponding member of "b" + # "a" contains an element whose value != the corresponding member of "b" local -a 'a=([5]=5 6 8 9 10)' 'b=({0..10})' isSubset a b echo $? # false @@ -465,7 +465,7 @@ dynamically calls a function whose name is resolved from the array. callFuncs() { # Set up indirect references as positional parameters to minimize local name collisions. - set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'} + set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'} # The only way to test for set but null parameters is unfortunately to test each individually. local x @@ -477,23 +477,23 @@ dynamically calls a function whose name is resolved from the array. [foo]='([r]=f [s]=g [t]=h)' [bar]='([u]=i [v]=j [w]=k)' [baz]='([x]=l [y]=m [z]=n)' - ) ${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["$1"]+"${1}=${!3}"}} # For example, if "$1" is "bar" then define a new array: bar=([u]=i [v]=j [w]=k) - ${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. + ${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() { # Define functions named {f..n} which just print their own names. - local fun='() { echo "$FUNCNAME"; }' x + local fun='() { echo "$FUNCNAME"; }' x for x in {f..n}; do - eval "${x}${fun}" + eval "${x}${fun}" done - callFuncs "$@" + callFuncs "$@" } - main "$@" + main "$@" ## 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 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. - declare -a a='([0]="foobork" [1]="barblarg" [2]="bazzooj" [3]="blah")' + 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. set -A a 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) $ ksh -c 'set -sA arr -- foo bar bork baz; typeset -p arr' # Native sorting! typeset -a arr=(bar baz bork foo) - $ 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. + $ 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 typeset arr[2]=baz 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 subscript assignment statement is expanded following the same rules as an ordinary assignment. This also works correctly in ksh93. - `$ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"' + `$ touch '[1]=a'; bash -c 'a=([1]=*); echo "${a[@]}"' [1]=a ` mksh has a similar but even worse problem in that the entire subscript is considered a glob. - `$ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"' + `$ touch 1=a; mksh -c 'a=([123]=*); print -r -- "${a[@]}"' 1=a ` - **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. - **Fixed in 4.3** Indirection combined with another modifier expands arrays to a single word. - `$ a=({a..c}) b=a[@]; printf '<%s> ' "${!b}"; echo; printf '<%s> ' "${!b/%/foo}"; echo + `$ a=({a..c}) b=a[@]; printf '<%s> ' "${!b}"; echo; printf '<%s> ' "${!b/%/foo}"; echo ` - **Fixed in 4.3** Process substitutions are evaluated within array indexes. Zsh and ksh don't do this in any arithmetic context. - `# print "moo" + `# print "moo" dev=fd=1 _[1<(echo moo >&2)]= # Fork bomb diff --git a/syntax/basicgrammar.md b/syntax/basicgrammar.md index 5176c3c..0063e41 100644 --- a/syntax/basicgrammar.md +++ b/syntax/basicgrammar.md @@ -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`: if ! grep '^root:' /etc/passwd; then - echo "No root user defined... eh?" + echo "No root user defined... eh?" fi 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 looks like: - print_help() { echo "Sorry, no help available"; } + print_help() { echo "Sorry, no help available"; } As above, a function definition can have any [compound command](basicgrammar#compound_commands) as a body. Structures like @@ -223,11 +223,11 @@ like this: mycmd() { # this $1 belongs to the function! - find / -iname "$1" + find / -iname "$1" } # this $1 belongs the script itself! - mycmd "$1" # Execute command immediately after defining function + mycmd "$1" # Execute command immediately after defining function exit 0 @@ -239,7 +239,7 @@ variables. Variables with the content "*() ....*". Something similar to the following works without "officially" declaring a function: - $ export testfn="() { echo test; }" + $ export testfn="() { echo test; }" $ bash -c testfn test $ @@ -284,7 +284,7 @@ FIXME more... A (very) simple command - echo "Hello world..." + echo "Hello world..." All of the following are simple commands @@ -292,7 +292,7 @@ FIXME more... >tmpfile - {x}<"$x" _=${x=<(echo moo)} <&0$(cat <&"$x" >&2) + {x}<"$x" _=${x=<(echo moo)} <&0$(cat <&"$x" >&2) ------------------------------------------------------------------------ diff --git a/syntax/ccmd/c_for.md b/syntax/ccmd/c_for.md index 73eb84d..dfedb2b 100644 --- a/syntax/ccmd/c_for.md +++ b/syntax/ccmd/c_for.md @@ -114,7 +114,7 @@ current value. for ((x = 0 ; x <= 100 ; x++)); do - echo "Counter: $x" + echo "Counter: $x" done ### 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**. for ((x = 0 ; x <= 100 ; x += 10)); do - echo "Counter: $x" + echo "Counter: $x" done ### Bits analyzer @@ -148,15 +148,15 @@ bits). function main { [[ $1 == +([0-9]) ]] || return typeset result - if (( $(ksh -c 'printf %..2d $1' _ "$1") == ( result = $(toBin "$1") ) )); then - printf '%s is %s in base 2!\n' "$1" "$result" + if (( $(ksh -c 'printf %..2d $1' _ "$1") == ( result = $(toBin "$1") ) )); then + printf '%s is %s in base 2!\n' "$1" "$result" else echo 'Oops, something went wrong with our calculation.' >&2 exit 1 fi } - main "${1:-123}" + main "${1:-123}" # vim: set fenc=utf-8 ff=unix ft=sh : @@ -191,7 +191,7 @@ demonstrating more complicated arithmetic expressions with multiple variables. for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);)); do - printf '%*s\n' "$((n+1))" "$n" + printf '%*s\n' "$((n+1))" "$n" done \ ~ \$ bash \<(xclip -o) 1 diff --git a/syntax/ccmd/case.md b/syntax/ccmd/case.md index 4985e0c..794d41d 100644 --- a/syntax/ccmd/case.md +++ b/syntax/ccmd/case.md @@ -36,7 +36,7 @@ expansion*; *arithmetic*, *command* and *process substitution*; and *quote removal*. **No word splitting, brace, or pathname expansion is done**, which means you can leave expansions unquoted without problems: - var="test word" + var="test word" case $var in ... @@ -78,7 +78,7 @@ Another one of my stupid examples... exit 1 ;; *) - echo "Unknown fruit - sure it isn't toxic?" + echo "Unknown fruit - sure it isn't toxic?" esac Here's a practical example showing a common pattern involving a `case` @@ -95,13 +95,13 @@ function clk { case $1 in low|high|default) - printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "old profile: $(<${base}/power_profile)" - echo "$1" >${base}/power_profile - echo "new profile: $(<${base}/power_profile)" + printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "old profile: $(<${base}/power_profile)" + echo "$1" >${base}/power_profile + echo "new profile: $(<${base}/power_profile)" ;; *) - echo "Usage: $FUNCNAME [ low | high | default ]" - printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "current profile: $(<${base}/power_profile)" + echo "Usage: $FUNCNAME [ low | high | default ]" + printf '%s\n' "temp: $(<${base}/hwmon/hwmon0/temp1_input)C" "current profile: $(<${base}/power_profile)" esac } ``` @@ -113,25 +113,25 @@ between blocks using `;&`, and the non-short-circuiting `;;&` operator: #!/usr/bin/env bash f() { - local -a "$@" + local -a "$@" local x for x; do case $x in $1) - local "$x"'+=(1)' ;;& + local "$x"'+=(1)' ;;& $2) - local "$x"'+=(2)' ;& + local "$x"'+=(2)' ;& $3) - local "$x"'+=(3)' ;; + local "$x"'+=(3)' ;; $1|$2) - local "$x"'+=(4)' + local "$x"'+=(4)' esac - IFS=, local -a "$x"'=("${x}: ${'"$x"'[*]}")' + IFS=, local -a "$x"'=("${x}: ${'"$x"'[*]}")' done for x; do - echo "${!x}" + echo "${!x}" done } diff --git a/syntax/ccmd/classic_for.md b/syntax/ccmd/classic_for.md index a6ed5c9..e8b2a81 100644 --- a/syntax/ccmd/classic_for.md +++ b/syntax/ccmd/classic_for.md @@ -71,8 +71,8 @@ With some array syntax (see [arrays](/syntax/arrays)) you can easily mass-expanding all elements): ``` bash -for element in "${myarray[@]}"; do - echo "Element: $element" +for element in "${myarray[@]}"; do + echo "Element: $element" done ``` @@ -80,8 +80,8 @@ Another way is to mass-expand all used indexes and access the array by index: ``` bash -for index in "${!myarray[@]}"; do - echo "Element[$index]: ${myarray[$index]}" +for index in "${!myarray[@]}"; do + echo "Element[$index]: ${myarray[$index]}" done ``` @@ -95,7 +95,7 @@ arguments to a command will be interpreted and parsed, and finally used: argtest() { n=1 for arg; do - echo "Argument $((n++)): \"$arg\"" + echo "Argument $((n++)): \"$arg\"" done } ``` @@ -108,16 +108,16 @@ filenames in a directory: ``` bash for fn in *; do - if [ -h "$fn" ]; then - echo -n "Symlink: " - elif [ -d "$fn" ]; then - echo -n "Dir: " - elif [ -f "$fn" ]; then - echo -n "File: " + if [ -h "$fn" ]; then + echo -n "Symlink: " + elif [ -d "$fn" ]; then + echo -n "Dir: " + elif [ -f "$fn" ]; then + echo -n "File: " else - echo -n "Unknown: " + echo -n "Unknown: " fi - echo "$fn" + echo "$fn" done ``` diff --git a/syntax/ccmd/grouping_plain.md b/syntax/ccmd/grouping_plain.md index 44056a6..1071424 100644 --- a/syntax/ccmd/grouping_plain.md +++ b/syntax/ccmd/grouping_plain.md @@ -22,10 +22,10 @@ status (exit code)](/scripting/basics#exit_codes) of the list. The input and output **filedescriptors** are cumulative: { - echo "PASSWD follows" + echo "PASSWD follows" cat /etc/passwd echo - echo "GROUPS follows" + echo "GROUPS follows" cat /etc/group } >output.txt @@ -34,10 +34,10 @@ definition](/syntax/basicgrammar#shell_function_definitions), though not the only compound command that's valid there: print_help() { - echo "Options:" - echo "-h This help text" - echo "-f FILE Use config file FILE" - echo "-u USER Run as user USER" + echo "Options:" + echo "-h This help text" + echo "-f FILE Use config file FILE" + echo "-u USER Run as user USER" } ## Examples @@ -46,10 +46,10 @@ the only compound command that's valid there: try_catch() { { # Try-block: - eval "$@" + eval "$@" } || { # Catch-block: - echo "An error occurred" + echo "An error occurred" return -1 } } diff --git a/syntax/ccmd/grouping_subshell.md b/syntax/ccmd/grouping_subshell.md index 895c519..739940f 100644 --- a/syntax/ccmd/grouping_subshell.md +++ b/syntax/ccmd/grouping_subshell.md @@ -15,9 +15,9 @@ etc...) are reflected in the "main shell". Execute a command in a different directory. ``` bash -echo "$PWD" -( cd /usr; echo "$PWD" ) -echo "$PWD" # Still in the original directory. +echo "$PWD" +( cd /usr; echo "$PWD" ) +echo "$PWD" # Still in the original directory. ``` ## Portability considerations diff --git a/syntax/ccmd/if_clause.md b/syntax/ccmd/if_clause.md index 508965f..385cdd1 100644 --- a/syntax/ccmd/if_clause.md +++ b/syntax/ccmd/if_clause.md @@ -51,15 +51,15 @@ commands of the condition that succeeded. **Check if a specific user exists in /etc/passwd :-)** if grep ^myuser: /etc/passwd >/dev/null 2>&1; then - echo "Yes, it seems I'm real" + echo "Yes, it seems I'm real" else - echo "Uh - am I a ghost?" + echo "Uh - am I a ghost?" fi **Mount with check** if ! mount /mnt/backup >/dev/null 2>&1; then - echo "FATAL: backup mount failed" >&2 + echo "FATAL: backup mount failed" >&2 exit 1 fi @@ -67,7 +67,7 @@ commands of the condition that succeeded. It's perfectly valid to do: - if echo "I'm testing!"; [ -e /some/file ]; then + if echo "I'm testing!"; [ -e /some/file ]; then ... 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 example above (multiple commands): - if echo "Hello world!" | grep -i hello >/dev/null 2>&1; then - echo "You just said 'hello', yeah?" + if echo "Hello world!" | grep -i hello >/dev/null 2>&1; then + echo "You just said 'hello', yeah?" fi ## Portability considerations diff --git a/syntax/expansion/arith.md b/syntax/expansion/arith.md index ed4a87b..6b60ef4 100644 --- a/syntax/expansion/arith.md +++ b/syntax/expansion/arith.md @@ -18,8 +18,8 @@ function printSum { typeset -A args typeset name for name in first second; do - [[ -t 0 ]] && printf 'Enter %s positive integer: ' "$name" >&2 - read -r ${BASH_VERSION+-e} "args[$name]" + [[ -t 0 ]] && printf 'Enter %s positive integer: ' "$name" >&2 + read -r ${BASH_VERSION+-e} "args[$name]" [[ ${args[$name]} == +([[:digit:]]) ]] || return 1 # Validation is extremely important whenever user input is used in arithmetic. done printf 'The sum is %d.' $((${args[first]} + ${args[second]})) @@ -49,7 +49,7 @@ x=1 echo $((x)) # Good. echo $(($x)) # Ok. Avoid expansions within arithmetic. Use variables directly. -echo $(("$x")) # Error. There is no quote-removal in arithmetic contexts. It expands to $(("1")), 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]})) # Ok. Nested expansion again. echo $((${x[$((${x[!$x]}-$x))]})) # Same as above but more ridiculous. diff --git a/syntax/expansion/brace.md b/syntax/expansion/brace.md index 6d4db53..845099c 100644 --- a/syntax/expansion/brace.md +++ b/syntax/expansion/brace.md @@ -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, another method is possible using [declaration commands](/commands/builtin/declare): -`declare -a 'pics=(img{'"$a..$b"'}.png)'; mv "${pics[@]}" ../imgs` +`declare -a 'pics=(img{'"$a..$b"'}.png)'; mv "${pics[@]}" ../imgs` This is significantly safer, but one must still be careful to control the values of \$a and \$b. Both the exact quoting, and explicitly including "-a" are important. @@ -181,18 +181,18 @@ for. - for i in 0{1..9} 10; do printf "%s\n" "$i";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 brace: - printf "%s\n" 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: - echo $(printf "img%02d.png " {1..99}) + echo $(printf "img%02d.png " {1..99}) 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 { [[ $1 == +([[:digit:]]) ]] || return typeset -a a - read -ra a < <(factor "$1") - eval "echo $(printf '{$(printf ,%%.s {1..%s})}' "${a[@]:1}")" + read -ra a < <(factor "$1") + eval "echo $(printf '{$(printf ,%%.s {1..%s})}' "${a[@]:1}")" } - printf 'eval printf "$arg"%s' "$(braceify 1000000)" + printf 'eval printf "$arg"%s' "$(braceify 1000000)" "Braceify" generates the expansion code itself. In this example we inject that output into a template which displays the most terse brace expansion code that would expand `"$arg"` 1,000,000 times if evaluated. In this case, the output is: - eval printf "$arg"{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,} + eval printf "$arg"{,,}{,,}{,,}{,,}{,,}{,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,}{,,,,,} \ diff --git a/syntax/expansion/cmdsubst.md b/syntax/expansion/cmdsubst.md index 19b7b6a..b35465e 100644 --- a/syntax/expansion/cmdsubst.md +++ b/syntax/expansion/cmdsubst.md @@ -73,7 +73,7 @@ substitution, while every `$()`-construct opens an own, subsequent parsing step. Everything inside `$()` is interpreted as if written normal on a commandline. No special escaping of **nothing** is needed: - echo "$(echo "$(ls)")" # nested double-quotes - no problem + echo "$(echo "$(ls)")" # nested double-quotes - no problem **Constructs you should avoid** @@ -91,10 +91,10 @@ substitution step and echo prints "ls" and ")": It seems that every closing ")" confuses this construct. Also a (very uncommon ;-)) construct like: - echo $(read VAR; case "$var" in foo) blah ;; esac) # spits out some error, when it sees the ";;" + echo $(read VAR; case "$var" in foo) blah ;; esac) # spits out some error, when it sees the ";;" # fixes it: - echo $(read VAR; case "$var" 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 ;-) **Conclusion:** @@ -110,19 +110,19 @@ In general, the `$()` should be the preferred method: **To get the date:** - DATE="$(date)" + DATE="$(date)" **To copy a file and get `cp` error output:** - COPY_OUTPUT="$(cp file.txt /some/where 2>&1)" + COPY_OUTPUT="$(cp file.txt /some/where 2>&1)" Attention: Here, you need to redirect `cp` `STDERR` to its `STDOUT` target, because command substitution only catches `STDOUT`! **Catch stdout and preserve trailing newlines:** - var=$(echo -n $'\n'); echo -n "$var"; # $var == "" - var=$(echo -n $'\n'; echo -n x); var="${var%x}"; echo -n "$var" # $var == "\n" + var=$(echo -n $'\n'); echo -n "$var"; # $var == "" + 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 previous commands' output from being deleted by \$(). diff --git a/syntax/expansion/globs.md b/syntax/expansion/globs.md index 72de9b1..a835395 100644 --- a/syntax/expansion/globs.md +++ b/syntax/expansion/globs.md @@ -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 `*.log`): - grep "changes:" *.log + grep "changes:" *.log The base syntax for the pathname expansion is the [pattern 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 **not** removed: - $ echo "Textfiles here:" *.txt + $ echo "Textfiles here:" *.txt Textfiles here: *.txt 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 filename in *.txt; do - echo "=== BEGIN: $filename ===" - cat "$filename" - echo "=== END: $filename ===" + echo "=== BEGIN: $filename ===" + cat "$filename" + echo "=== END: $filename ===" done 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: $ shopt -s nullglob - $ echo "Textfiles here:" *.txt + $ echo "Textfiles here:" *.txt Textfiles here: and the glob is gone. diff --git a/syntax/expansion/intro.md b/syntax/expansion/intro.md index ce37501..f87c980 100644 --- a/syntax/expansion/intro.md +++ b/syntax/expansion/intro.md @@ -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: - mystring="Hello world" - echo "$mystring" + mystring="Hello world" + echo "$mystring" 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 diff --git a/syntax/expansion/proc_subst.md b/syntax/expansion/proc_subst.md index 0b38dca..d4327a0 100644 --- a/syntax/expansion/proc_subst.md +++ b/syntax/expansion/proc_subst.md @@ -71,7 +71,7 @@ file `/dev/fd/63`. Consider the following: ``` bash -diff <(ls "$first_directory") <(ls "$second_directory") +diff <(ls "$first_directory") <(ls "$second_directory") ``` 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++)) done -echo "$counter files" # prints "0 files" +echo "$counter files" # prints "0 files" ``` Due to the pipe, the `while read; do ... done` part is executed in a @@ -121,7 +121,7 @@ while IFS= read -rN1 _; do ((counter++)) done < <(find /etc -printf ' ') -echo "$counter files" +echo "$counter files" ``` 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 f() { - cat "$1" >"$x" + cat "$1" >"$x" } 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) ``` bash -# print "moo" +# print "moo" dev=fd=1 _[1<(echo moo >&2)]= # fork bomb ${dev[${dev='dev[1>(${dev[dev]})]'}]} diff --git a/syntax/expansion/tilde.md b/syntax/expansion/tilde.md index 98d55e9..08cd80b 100644 --- a/syntax/expansion/tilde.md +++ b/syntax/expansion/tilde.md @@ -49,7 +49,7 @@ This way you can correctly use the tilde expansion in your **Spaces in the referenced pathes?** A construct like... - ~/"my directory" + ~/"my directory" ...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, which holds the currect working directory: - echo "CWD is $PWD" + echo "CWD is $PWD" is equivalent to (note it **must** be a separate word!): - echo "CWD is" ~+ + echo "CWD is" ~+ ## Previous working directory diff --git a/syntax/grammar/parser_exec.md b/syntax/grammar/parser_exec.md index c83f1ce..9490267 100644 --- a/syntax/grammar/parser_exec.md +++ b/syntax/grammar/parser_exec.md @@ -93,7 +93,7 @@ The behavior regarding the variable assignment errors can be tested: echo PRE # The following is an assignment error! - # The "echo TEST" won't be executed, since the { ...; } is terminated + # The "echo TEST" won't be executed, since the { ...; } is terminated { foo=$((8#9)); echo TEST; } echo POST diff --git a/syntax/keywords/coproc.md b/syntax/keywords/coproc.md index 6b156aa..dfa7936 100644 --- a/syntax/keywords/coproc.md +++ b/syntax/keywords/coproc.md @@ -35,13 +35,13 @@ Some examples: # redirecting stderr in the pipe $ coproc { ls thisfiledoesntexist; read; } 2>&1 [2] 23084 -$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x" +$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x" ls: cannot access thisfiledoesntexist: No such file or directory ``` ``` bash #let the output of the coprocess go to stdout -$ { coproc mycoproc { awk '{print "foo" $0;fflush()}'; } >&3; } 3>&1 +$ { coproc mycoproc { awk '{print "foo" $0;fflush()}'; } >&3; } 3>&1 [2] 23092 $ echo bar >&${mycoproc[1]} $ foobar @@ -64,7 +64,7 @@ In Ksh you would do: ``` bash # ksh93 or mksh/pdksh derivatives ls |& # start a coprocess -while IFS= read -rp file; do print -r -- "$file"; done # read its output +while IFS= read -rp file; do print -r -- "$file"; done # read its output ``` In bash: @@ -73,7 +73,7 @@ In bash: #DOESN'T WORK $ coproc ls [1] 23232 -$ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' "$line"; done +$ while IFS= read -ru ${COPROC[0]} line; do printf '%s\n' "$line"; done bash: read: line: invalid file descriptor specification [1]+ Done coproc COPROC ls ``` @@ -95,7 +95,7 @@ with `sed`: $ coproc sed s/^/foo/ [1] 22981 $ echo bar >&${COPROC[1]} -$ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo "nothing read" +$ read -t 3 -ru ${COPROC[0]} _; (( $? > 127 )) && echo "nothing read" nothing read ``` @@ -116,9 +116,9 @@ continuously read the output of a coprocess and `echo` the result: ``` bash #NOT WORKING -$ coproc awk '{print "foo" $0;fflush()}' +$ coproc awk '{print "foo" $0;fflush()}' [2] 23100 -$ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' "$x"; done & +$ while IFS= read -ru ${COPROC[0]} x; do printf '%s\n' "$x"; done & [3] 23104 bash: line 243: read: 61: invalid file descriptor: Bad file descriptor ``` @@ -132,10 +132,10 @@ A possible workaround: #WARNING: for illustration purpose ONLY # this is not the way to make the coprocess print its output # to stdout, see the redirections above. -$ coproc awk '{print "foo" $0;fflush()}' +$ coproc awk '{print "foo" $0;fflush()}' [2] 23109 $ exec 3<&${COPROC[0]} -$ while IFS= read -ru 3 x; do printf '%s\n' "$x"; done & +$ while IFS= read -ru 3 x; do printf '%s\n' "$x"; done & [3] 23110 $ echo bar >&${COPROC[1]} $ foobar @@ -152,7 +152,7 @@ assigns FDs to a default array named `COPROC` if no `NAME` is supplied. Here's an example: ``` bash -$ coproc awk '{print "foo" $0;fflush()}' +$ coproc awk '{print "foo" $0;fflush()}' [1] 22978 ``` @@ -167,7 +167,7 @@ $ echo bar >&${COPROC[1]} And then read its output: ``` bash -$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x" +$ IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x" foobar ``` @@ -175,7 +175,7 @@ When we don't need our command anymore, we can kill it via its pid: $ kill $COPROC_PID $ - [1]+ Terminated coproc COPROC awk '{print "foo" $0;fflush()}' + [1]+ Terminated coproc COPROC awk '{print "foo" $0;fflush()}' ### 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. ``` bash -$ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;} +$ coproc mycoproc { awk '{print "foo" $0;fflush()}' ;} [1] 23058 $ echo bar >&${mycoproc[1]} -$ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' "$x" +$ IFS= read -ru ${mycoproc[0]} x; printf '%s\n' "$x" foobar $ kill $mycoproc_PID $ -[1]+ Terminated coproc mycoproc { awk '{print "foo" $0;fflush()}'; } +[1]+ Terminated coproc mycoproc { awk '{print "foo" $0;fflush()}'; } ``` ### Redirecting the output of a script to a file and to the screen diff --git a/syntax/pe.md b/syntax/pe.md index 4ed97e1..fab9034 100644 --- a/syntax/pe.md +++ b/syntax/pe.md @@ -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 word with a trailing "s": - echo "The plural of $WORD is most likely $WORDs" - echo "The plural of $WORD is most likely ${WORD}s" + echo "The plural of $WORD is most likely $WORDs" + echo "The plural of $WORD is most likely ${WORD}s" Why does the first one fail? It prints nothing, because a 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 positional parameters (arguments to a script) beyond `$9`: - echo "Argument 1 is: $1" - echo "Argument 10 is: ${10}" + echo "Argument 1 is: $1" + echo "Argument 10 is: ${10}" ### 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 - printf 'The value of "%s" is: "%s"\n' "$look_var" "${!look_var}" + printf 'The value of "%s" is: "%s"\n' "$look_var" "${!look_var}" Of course the indirection also works with special variables: # set some fake positional parameters set one two three four - # get the LAST argument ("#" stores the number of arguments, so "!#" will reference the LAST argument) + # get the LAST argument ("#" stores the number of arguments, so "!#" will reference the LAST argument) echo ${!#} You can think of this mechanism as being roughly equivalent to taking any parameter expansion that begins with the parameter name, and substituting the `!PARAMETER` part with the value of PARAMETER. - echo "${!var^^}" + echo "${!var^^}" # ...is equivalent to - eval 'echo "${'"$var"'^^}"' + eval 'echo "${'"$var"'^^}"' It was an unfortunate design decision to use the `!` prefix for 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 **Example: Rename all `*.txt` filenames to lowercase** for file in *.txt; do - mv "$file" "${file,,}" + mv "$file" "${file,,}" done **Note:** Case modification is a handy feature you can apply to a @@ -255,7 +255,7 @@ Assume: `array=(This is some Text)` - * ''echo "${array[2]^^}"'' + * ''echo "${array[2]^^}"'' * => ''SOME'' ## 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*): - MYSTRING="Be liberal in what you accept, and conservative in what you send" + MYSTRING="Be liberal in what you accept, and conservative in what you send" ### 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 example string for the tests: - MYSTRING="Be liberal in what you accept, and conservative in what you send" + 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 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. Again, a quote from a big man, to have a test text: - MYSTRING="Be liberal in what you accept, and conservative in what you send" + MYSTRING="Be liberal in what you accept, and conservative in what you send" 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 then: - echo "${MYSTRING:11:-17}" + echo "${MYSTRING:11:-17}" =\> `Be liberal in what you accept, and conservative in what you send` @@ -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 when the parameter was **unset**, not when it was empty. - echo "Your home directory is: ${HOME:-/home/$USER}." - echo "${HOME:-/home/$USER} will be used to store your personal data." + echo "Your home directory is: ${HOME:-/home/$USER}." + echo "${HOME:-/home/$USER} will be used to store your personal data." If `HOME` is unset or empty, everytime you want to print something useful, you need to put that parameter syntax in. #!/bin/bash - read -p "Enter your gender (just press ENTER to not tell us): " GENDER - echo "Your gender is ${GENDER:-a secret}." + read -p "Enter your gender (just press ENTER to not tell us): " GENDER + echo "Your gender is ${GENDER:-a secret}." 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 @@ -650,14 +650,14 @@ Example code (please try the example cases yourself): ### CASE 3: An array with only one element, a nullstring - array=("") + array=("") 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 - array=("" word) + array=("" word) 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 assigned when the parameter was **unset**. - echo "Your home directory is: ${HOME:=/home/$USER}." - echo "$HOME will be used to store your personal data." + echo "Your home directory is: ${HOME:=/home/$USER}." + echo "$HOME will be used to store your personal data." After the first expansion here (`${HOME:=/home/$USER}`), `HOME` is set and usable. @@ -685,9 +685,9 @@ Let's change our code example from above: #!/bin/bash - read -p "Enter your gender (just press ENTER to not tell us): " GENDER - echo "Your gender is ${GENDER:=a secret}." - echo "Ah, in case you forgot, your gender is really: $GENDER" + read -p "Enter your gender (just press ENTER to not tell us): " GENDER + echo "Your gender is ${GENDER:=a secret}." + echo "Ah, in case you forgot, your gender is really: $GENDER" ### 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 you can specify**: - echo "The Java application was installed and can be started.${JAVAPATH:+ NOTE: JAVAPATH seems to be set}" + 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 it could influence the startup behaviour of that imaginary application). @@ -718,8 +718,8 @@ flags: #!/bin/bash - read -p "If you want to use special flags, enter them now: " SPECIAL_FLAGS - echo "The installation of the application is finished${SPECIAL_FLAGS:+ (NOTE: there are special flags set: $SPECIAL_FLAGS)}." + read -p "If you want to use special flags, enter them now: " SPECIAL_FLAGS + 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 (`${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: # unset foo - # foo="" - # foo="something" + # foo="" + # foo="something" if [[ ${foo+isset} = isset ]]; then - echo "foo is set..." + echo "foo is set..." else - echo "foo is not set..." + echo "foo is not set..." fi ### 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 for an error message: - $ echo "The unset parameter is: ${p_unset?not set}" + $ echo "The unset parameter is: ${p_unset?not set}" bash: p_unset: not set After printing this message, @@ -785,13 +785,13 @@ are taken into account. Removing the first 6 characters from a text string: - STRING="Hello world" + STRING="Hello world" # only print 'Hello' - echo "${STRING%??????}" + echo "${STRING%??????}" # only print 'world' - echo "${STRING#??????}" + echo "${STRING#??????}" # store it into the same variable 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 argument. As a workaround, each expansion needs to be quoted separately. Unfortunately, this bug took a very long time to - notice.`~ $ set -- a b c; x=foo; printf '<%s> ' "$@$x" "$*""$x" "$@""$x" + notice.`~ $ set -- a b c; x=foo; printf '<%s> ' "$@$x" "$*""$x" "$@""$x" ` @@ -823,14 +823,14 @@ Removing the first 6 characters from a text string: unquoted, this should be a rare issue. **Always quote them**!`touch x 'y z' for sh in bb {{d,b}a,{m,}k,z}sh; do - echo "$sh" - "$sh" -s a 'b c' d \* ' $* echo - printf "<%s> " $@ + printf "<%s> " $@ echo EOF ``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 middle of expansion through side-effects.`for sh in bb {{d,b}a,po,{m,}k,z}sh; do - printf '%-4s: ' "$sh" - "$sh" ' ${*}${IFS=}${*}${IFS:=-}"${*}" + printf '<%s> ' ${*}${IFS=}${*}${IFS:=-}"${*}" echo EOF ``bb : @@ -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 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. - ` $ 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="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([5]=hi); echo "${x[@]:n,6:m}"' + ` $ 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="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([5]=hi); echo "${x[@]:n,6:m}"' yo - $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([6]=hi); echo "${x[@]:n,6:m}"' + $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([6]=hi); echo "${x[@]:n,6:m}"' yojo - $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,5:m}"' + $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,5:m}"' yojo - $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,6:m}"' + $ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=12345; echo "${x:n,6:m}"' yo ` @@ -935,12 +935,12 @@ Removing the first 6 characters from a text string: # Bash $ typeset -a a=(meh bleh blerg) b $ IFS=e - $ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}"; 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. - $ printf "<%s> " "${b[@]-${a[@]} ${a[@]}}"; 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. $ b=(meep beep) - $ printf "<%s> " "${b[@]-"${a[@]}" "${a[@]}"}" "${b[@]-${a[@]} ${a[@]}}"; 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. 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 $ typeset -a a=(meh bleh blerg) b $ IFS=e - $ printf "<%s> " ${b[@]-"${a[@]}" "${a[@]}"}; echo # Inner quotes make inner expansions quoted. + $ printf "<%s> " ${b[@]-"${a[@]}" "${a[@]}"}; echo # Inner quotes make inner expansions quoted. - $ printf "<%s> " ${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. 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 will actually happen... - "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5 + "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5 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 @@ -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: # The quoted PE produces a correct result here... - $ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo' + $ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo' <4> # ...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 "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo' + $ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " "${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"}" arg5; echo' # UNLESS! we unquote the outer expansion allowing the inner quotes to # affect the necessary parts while allowing word-splitting to split the literals: - $ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo' + $ bash -c 'typeset -a otherArgs=(arg3 arg4); someOtherCmd=mycommand; printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo' # Success!!! - $ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo' + $ bash -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " ${someCmd[@]-"$someOtherCmd" arg2 "${otherArgs[@]}"} arg5; echo' <4> # ...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: - $ ksh -c 'otherArgs=(arg3 arg4); someOtherCmd="mycommand"; printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo' + $ ksh -c 'otherArgs=(arg3 arg4); someOtherCmd="mycommand"; printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo' - $ ksh -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo' + $ ksh -c 'typeset -a someCmd=(myCmd arg1 "arg2 yay!" "third*arg*" 4); printf "<%s> " "${someCmd[@]-""$someOtherCmd" arg2 "${otherArgs[@]}""}" arg5; echo' <4> This can be used to control the quote state of any part of any expansion diff --git a/syntax/quoting.md b/syntax/quoting.md index cb1c484..4e3d5a6 100644 --- a/syntax/quoting.md +++ b/syntax/quoting.md @@ -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: ### NO NO NO: this passes three strings: - ### (1) "my + ### (1) "my ### (2) multiword - ### (3) argument" - MYARG="\"my multiword argument\"" + ### (3) argument" + MYARG="\"my multiword argument\"" somecommand $MYARG ### THIS IS NOT (!) THE SAME AS ### - command "my multiword argument" + command "my multiword argument" ### YOU NEED ### - MYARG="my multiword argument" - command "$MYARG" + MYARG="my multiword argument" + command "$MYARG" ## 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 backslash: - echo \$HOME is set to \"$HOME\" + echo \$HOME is set to \"$HOME\" - `\$HOME` won't expand because it's not in variable-expansion syntax anymore @@ -81,12 +81,12 @@ Inside a weak-quoted string there's **no special interpretion of**: Everything else, especially [parameter expansion](/syntax/pe), is performed! - ls -l "*" + ls -l "*" Will not be expanded. `ls` gets the literal `*` as argument. It will, unless you have a file named `*`, spit out an error. - echo "Your PATH is: $PATH" + echo "Your PATH is: $PATH" Will work as expected. `$PATH` is expanded, because it's double (weak) quoted. @@ -126,7 +126,7 @@ to get the character "`'`" as literal text: echo 'Here'\''s my test...' # ALTERNATIVE: It's also possible to mix-and-match quotes for readability: - echo "Here's my test" + echo "Here's my test" ## ANSI C like strings @@ -174,7 +174,7 @@ compatibility" features). A dollar-sign followed by a double-quoted string, for example - echo $"generating database..." + echo $"generating database..." 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`, @@ -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 iterate through. The list can also be in a variable: - mylist="DOG CAT BIRD HORSE" + mylist="DOG CAT BIRD HORSE" **WRONG** way to iterate through this list: - for animal in "$mylist"; do + for animal in "$mylist"; do echo $animal 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 string with spaces: - mystring="my string" + mystring="my string" 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: - [ "$mystring" = testword ] # RIGHT! + [ "$mystring" = testword ] # RIGHT! test 'my string' = testword diff --git a/syntax/redirection.md b/syntax/redirection.md index b22ae19..1a653bd 100644 --- a/syntax/redirection.md +++ b/syntax/redirection.md @@ -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: # this executes the echo-command and redirects its normal output (stdout) to the standard error target - echo "There was an error" 1>&2 + echo "There was an error" 1>&2 The redirection operation can be **anywhere** in a simple command, so 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: - cat <<"EOF" + cat <<"EOF" This won't be expanded: $PATH EOF @@ -191,7 +191,7 @@ end-of-file is seen before the tag is reached.\ The here-strings are a variation of the here-documents. The word `WORD` is taken for the input redirection: - cat <<< "Hello world... $NAME is here..." + cat <<< "Hello world... $NAME is here..." Just beware to quote the `WORD` if it contains spaces. Otherwise the rest will be given as normal parameters. diff --git a/syntax/shellvars.md b/syntax/shellvars.md index fb993e3..dcb6027 100644 --- a/syntax/shellvars.md +++ b/syntax/shellvars.md @@ -1238,7 +1238,7 @@ current mailfile. Example content: - /var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!" + /var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!" ### OPTERR diff --git a/syntax/words.md b/syntax/words.md index 38d54d0..4d53676 100644 --- a/syntax/words.md +++ b/syntax/words.md @@ -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, both cause Bash to not treat spaces as special characters: - $ echo "Hello little world" + $ echo "Hello little world" Hello little world $ echo 'Hello little world' @@ -80,7 +80,7 @@ spaces in them: $ cat test\ file m00! - $ cat "test file" + $ cat "test file" m00! 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: - MYFILE="test file" + MYFILE="test file" When this variable is used, its occurance will be replaced by its 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 word splitting: - $ cat "$MYFILE" + $ cat "$MYFILE" m00! ## Example @@ -124,7 +124,7 @@ word splitting: Let's follow an unquoted command through these steps, assuming that the variable is set: - MYFILE="THE FILE.TXT" + MYFILE="THE FILE.TXT" 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: - echo The file is named "$MYFILE" + echo The file is named "$MYFILE" | Word splitting after substitution (quoted!): | | | | | | |----------------------------------------------|--------|--------|--------|---------|----------------|