====== Bash Programming ==
===== Documentation ==
- [[kman>man1/bash.1|Bash man page]]
- [[http://tldp.org/LDP/abs/html/|Advanced Bash-Scripting Guide]]
===== File Inclusion, Command Execution ==
**source script (or: . script)**
Read and execute commands from filename in the current shell environment and return the exit status of the last command executed from filename If any arguments are supplied, they become the positional parameters when filename is executed. Otherwise the positional parameters are unchanged From man bash
**exec command **
replaces the shell. No new process is created (man bash) replaces the current (shell)-process with command (abs-guide)
**command**
when the shell encounters a command, it forks off a child process to actually execute the command (external commands only; from abs-guide). Parent script blocks until child process ends
**command &**
forks off a child process to execute the command and put child process into background so that the parent script continues immediately
**eval command ...**
man bash: Command ist read and executed by the shell
For me useful if command contains spaces witch are protected by single quotes, eg ''%%wget -S --header'Content-Type: application/json'%%''. Without eval, ''%%--header'Content-Type:%%'' and ''%%application/json'%%'' would be interpreted as 2 single arguments
===== Subshells ==
FIXME
===== Coprocesses ==
The following example starts a shell function as a coproc and uses the coproc to manipulate a string:
#!/bin/bash
test_coproc() {
read rein
echo ">$rein<"
}
coproc test_coproc
echo "Hallo" >&${COPROC[1]}
read -u ${COPROC[0]} rein
echo "$rein"
#cat <&${COPROC[0]} # alternative
===== Expansion ==
FIXME doc ''*'' and ''?''
echo {a,}quota.{group,user}.new
# prints: aquota.group.new aquota.user.new quota.group.new quota.user.new
# without any regard to the content of the working directory
===== Variables ==
$meli is a shortcut for ${meli}
==== Variable Variables ==
# get a value from a variable (meli) over a 'pointer' (who): (from abs-guide)
meli=Melanie
who=meli # who is used as pointer to meli
adr=${!who} # now $adr contains Melanie
# set a value into a variable (meli) over a 'pointer' (who): (tried myself)
meli=""
who=meli
eval $(echo $who)=Melanie # now $meli contains Melanie
==== Special Variables ==
$# # number of command line arguments
$* # Arguments as one word
$@ # Arguments as separate words
$$ # Pid of script
$? # return value of command (& function?)
$! # PID of last job run in background
$0 # full name of script
$1 # first argument of script
Following a ''shift'' , the ''$@'' holds the remaining command-line parameters, lacking the previous ''$1'', which was lost(([[tldp>LDP/abs/html/othertypesv.html#EX19|Using shift]])).
==== Default Values ==
If variable is not set, expand to default value:
${variable:-defaultvalue}
# Example:
#Keep Value of VERSION or if not set yet, set it to 3.9:
VERSION=${VERSION:-3.9}
[[tldp>LDP/abs/html/parameter-substitution.html|Source]]
===== Arrays ==
#!/bin/bash
# first index is 0
app=( XMMS XINE XAWTV )
appcount=${#app[*]} # 3
appix=1
echo ${app[appix]} # XINE
===== Maps ==
See http://stackoverflow.com/questions/1494178/how-to-define-hash-tables-in-bash
===== Loops ==
while
read -p 'Search term (English or German): ' inp
test "$inp"
do
query $inp
done
for (( ix=10 ; ix<21 ; ix++ )) ; do
echo $ix
done
for arg in "$@"; do # $@ sees arguments as separate words.
echo $arg
done
# Loop through all ogg files of current dir. This is whitepace-save
for fn in *.ogg; do
echo $fn
done
# Print each word (separated by whitespace) in a textfile into one line (tested):
for token in $(cat textfile); do
echo $token
done
# Loop through an array...
quote=('Die Mutter' 'der Dummen' 'ist immer' schwanger)
# without index:
for token in "${quote[@]}"; do
echo $token
done
# with index:
for ((i=0; i<${#quote[@]}; i++)); do
echo "$i: ${quote[i]}"
done
===== Quoting ==
aVar="Otto"
echo -e "$aVar\nnext line"
echo -e '$aVar\nnext line'
Result:
Otto
next line
$aVar
next line
This means:
* Backslashes are always interpreted
* Variables are ony substituted between double quotes
===== Functions ==
==== Parameters ==
FIXME
myfunc() {
echo $* #-> eins zwei drei
echo $@ #-> eins zwei drei
echo $1 #-> eins
}
myfunc eins zwei drei # call the function
==== Exit Status and return Value ==
The exit status may be explicitly specified by a return statement, otherwise it is the exit status of the last command in the function. Exit status can be checked with ''$?''.
Here is a method to return a string along with the exit status:
myfunc() {
echo "Hello $1"
}
res=$(myfunc 'crazy world') # Oddly no quotes necessary in bash and ash
echo ">>$res<<"
By using a separator (tab) and the cut command it is possible to return more than one value:
myfunc() {
echo -e "EINS\tZWEI"
}
buf=$(myfunc)
eins=$(echo "$buf" | cut -f1)
zwei=$(echo "$buf" | cut -f2)
echo "$eins - $zwei" #-> EINS - ZWEI
===== Branching: if then else ==
Be aware of this:
if true; then echo "true=$?"; else echo "false=$?"; fi # prints true=0 !
if false; then echo "true=$?"; else echo "false=$?"; fi # prints false=1 !
Examples:
if [ -d $arg ]; then # a test (see below); mind the spaces!
...
elif ! command; then
...
elif [ "$a" == "$b" ] && [ "c" != "d" ]; then # also ||
...
elif [ -z "$aString" ]; then # true if length==0
...
fi
===== Tests =====
-d file True if file exists and is a directory.
-e file True if file exists.
-f file True if file exists and is a regular file.
-h file True if file exists and is a symbolic link.
-r file True if file exists and is readable.
-w file True if file exists and is writable.
-s file True if file exists and has a size greater than zero
-x file has execute permission (for the user running the test)
-z string is null, that is, has zero length
-n string is not null, that is, has not zero length
# Example:
if [ ! -r "$sourcefile" ]; then
echo "Error: Cant read $sorcefile!"
exit 2
fi
See also: [[man>test]] and [[man>bash]] -> "Conditional expressions"
===== Exit status of a command ==
command
if [ $? -ne 0 ]; then
echo "got an error!"
fi
#or:
command
if [ $? -eq 0 ]; then
echo "command was successful"
fi
''abort'', my useful helper function:
abort () {
if [ -n "$1" ]; then echo "$(basename $0): $1" >&2; fi
if [ -n "$2" ]; then err="$2"; else err=1; fi
exit $err
}
# usage examples:
[ -d "$SOURCE" ] || abort "Source \"$SOURCE\" is not a directory"
a_command || abort "Command failed" 7
a_long_command
[ $? -eq 0 ] || abort "long command failed"
===== Redirection ==
command > file.txt # stdout 2 file
command 2> file.txt # stderr 2 file
command 1>&2 # stdout 2 stderr
command >&2 # stdout 2 stderr ?
command 2>&1 # stderr 2 stdout
command &> /dev/null # stderr & stdout 2 file (suppress output here)