Index

Table of contents

Dash / Bourne Shell / POSIX shell

flags

run syntax check on file
sh -n [file]
run script in bourne shell interpreter, ignore errors
sh [file]
run script and fail on error
sh -e [file]
debug script (print every command as executed)
sh -x [file]
debug script (print every line as read)
sh -v [file]
fail when a undefined variable is referenced
sh -u [file]
auto export variables on set/change (makes them available to invoked scripts)
set -a
modify flag in the middle of a script
set [+-][flag]
example: fail on error
set -e
example: ignore errors (default)
set +e
example: print every command
set -x
example: no debug logging (default)
set +x

comments

comment out single line
# ignored
comment out multiple lines
false && {
	[command1]
	[command2]
	[command3]
	...
}

parameters

echo first parameter
echo "$1"
echo second parameter
echo "$2"
echo all parameters
echo "$@"
drop first parameter and shift all parameters left
shift
echo "$1"    # echoes what was originally $2
drop n parameters
shift [n]
echo "$1"    # echoes what was originally $[n+1]
reassign all parameters
set [$1] [$2] [...]
use variables if parameters contain spaces in them:
variable="has space"
set "$variable"

variables

assign variable
KEY="value"
if variable undefined terminate script, print supplied error message
${[variable]?[message]}
use default if variable undefined
${[variable]-[default]}
set variable to default if undefined
${[variable]=[default]}
use default if variable IS defined (useful for debugging)
${[variable]+[default]}
add a colon to the tests above to include empty variables in the test
${[variable]:?[message]}
${[variable]:-[default]}
${[variable]:=[default]}
${[variable]:+[default]}
note that you can use variables & subshells in the default, example:
${var-$other}
${var-$(echo foo)}
${var+"value of var: $var"}
remove smallest matching suffix
${parameter%[wildcard expression]}
remove largest matching suffix
${parameter%%[wildcard expression]}
remove smallest matching prefix
${parameter#[wildcard expression]}
remove largest matching prefix
${parameter##[wildcard expression]}
get the length of a variable
${#[variable]}
assign exit code of last command
CODE=$?
store result of method call in variable
LS=$(ls -l)
PLUS="before $(echo between) after"
execute string or variable as command (eval)
sh -c "[command]"
echo "[command]" | sh
evalating a string in a variable as code
variable='echo "bla" > /tmp/bla'
eval "$variable"
Passing a named variable to another script
NAME=VALUE
. /path/to/other/script.sh
note: '.' means source, then a space, then the path to the script, $NAME will be available

Environment Variables

dump environment variables
printenv
print specific environment variable
printenv HOSTNAME
set a variable
variable=value
export variable as environment variable
export $variable
set and export environment variable
export variable=value
unset a variable
unset [variable]

Processes

own PID (from within the script itself to grab its own PID)
$$
pid of previously executed process
$!
return value of previous process
$?
wait until all background jobs are finished
wait
wait until most recent background job is finished
wait $!
wait until proces with a specific id is finished
wait [pid]
run jobs in parallel
cat args.txt | xargs -P [threads] -n 1 echo
cat args.txt | parallel -j [threads] echo {}

functions

define function
example() {
	echo "hello world!"
}
pass param to function
example () {
   echo "Parameter #1 is $1"
}
example foo
if calling another function for a decision
function f1() {
  return 0 # = success, in other words yes for if/else
}
if f1; then
the first command in a function reads piped contents
myfunc (){
  while read data; do
      echo "$data"
  done
}
cat [file] | myfunc
assigning piped contents to a variable
myfunc () {
         body=$(cat)
}

Control Flow

statements

execute sequentially
[a];[b]
execute in parallel
[a]&[b]
execute next only if status code = 0 (succes)
[a]&&[b]
execute next only if status code != 0 (error code)
[a]||[b]

if else

if [ conditional expression ]
then
    statement
    statement
elif [ conditional expression ]
then
    statement
    statement
else
    statement
    statement
fi
if else with return value of another function
if tomcat_running; then
	die "port $port already taken"
call another function inside an if statement
if [ $(echo 'match') = "match" ]; then
	echo "this works!"
fi
execute function body only once
UP_TO_DATE=false

update_once() {
    if [ $UP_TO_DATE -eq false ]; then
        echo "just once"
        UP_TO_DATE=true
    fi
}
execute "then" if command has output
if (echo "hello" )
execute "else" otherwise
if (echo "hello" | grep appel )

case statement

case value in
	pattern0 ) command-list-0 ;;
	pattern1 ) command-list-1 ;;
	...
esac
before the parenthesis use regular expressions:
case "$1" in
        [yY]|[yY]es) echo "affermative";;
        [nN]|[nN]o) echo "negative";;
        [yY]*) echo "misspelled yes";;
        [nN]*) echo "misspelled no";;
        *) echo "unknown";;
esac

while loop

loop until condition is false
while
	command-list1
do
	command-list2
done
example: requesting user input
echo "Please type the arguments"
echo "Type Control-D (EOF) when done"
args="":
while
	echo '?'
	read a
do
	args="$args $a"
done
echo "The arguments are $args"
example: iterating with index
A=1
while [ "$A" -le 10 ]
do
        echo $A
        A=$((A+1))
done
loop until condition is true
until
        command-list1
do
        command-list2
done

for each

for each (space separated argument) with raw input strings
for i in 1 2 3 4 5; do echo "repeat $i"; done
for i in value{1,2,3}; do echo "repeat $i"; done
for each loop (line separated)
while read i; do echo "$i"; done < [file]
[command] | while read -r line; do echo "$line"; done
for each loop (line separated) separating result in variables
pgrep -af java | while read -r pid cmd; do echo "$pid => $cmd"; done
for each whitespace separated entry resulting from another shell command
for line in `cat lines`; do echo $line; done
for line in $(cat lines); do echo $line; done
for each with pipe
for line in $(cat lines); do echo $line | grep js; done
for each loop with wildcard
for file in *.jpg;
do
    echo "$file"
done
for each with multiple statements
for line in $(cat lines); do echo "$line"; touch "$line"; done
the same as before with pretty formatting (in console or script)
for line in $(cat lines); do
     echo "$line"
     touch "$line"
done
executing statements conditionally based on the output of a command
for file in $(ls)
do
  grep -q "a" $file && grep -q "b" $file && echo $file
done
echo all command line arguments to a script from within the script
for a in $@; do echo "arg $a"; done
echo all command line arguments, skip the first (shift drops first argument)
shift
for a in $@; do echo "arg $a"; done
echo all command line arguments, skip the first [n]
shift [n]
for a in $@; do echo "arg $a"; done

break

skip the rest of this (inner most) loop iteration
continue
exit the inner most loop
break
break out of the loop at depth level n (1 = inner most loop)
break [n]
use return to exit the top level function regardless of loop nesting depth
return 0

documentation links

specfication
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
return values from functions
https://www.linuxjournal.com/content/return-values-bash-functions
tutorials
https://en.wikibooks.org/wiki/Bourne_Shell_Scripting
http://steve-parker.org/sh/sh.shtmL
http://www.grymoire.com/Unix/Sh.html
work arounds
http://www.etalabs.net/sh_tricks.html