Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
becki:linux:bash [2010-12-24 15:49] becki |
becki:linux:bash [2016-03-03 11:25] (aktuell) becki [File Inclusion, Command Execution] |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
- | ====== Bash Programming ====== | + | ====== Bash Programming == |
+ | ===== Documentation == | ||
+ | - [[kman>man1/bash.1|Bash man page]] | ||
+ | - [[http://tldp.org/LDP/abs/html/|Advanced Bash-Scripting Guide]] | ||
===== File Inclusion, Command Execution == | ===== File Inclusion, Command Execution == | ||
Zeile 13: | Zeile 16: | ||
**command** | **command** | ||
- | when the shell encounters a command, it forks off a child process to actually execute the command (external commands only; from abs-guide) FIXME: Is this true? Isn't this ''%%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 ...** | **eval command ...** | ||
Zeile 21: | Zeile 28: | ||
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 | 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 | ||
- | ===== Arrays == | + | ===== Subshells == |
+ | |||
+ | FIXME | ||
+ | |||
+ | ===== Coprocesses == | ||
+ | |||
+ | The following example starts a shell function as a coproc and uses the coproc to manipulate a string: | ||
<code bash> | <code bash> | ||
#!/bin/bash | #!/bin/bash | ||
- | # first index is 0 | + | test_coproc() { |
- | app=( XMMS XINE XAWTV ) | + | read rein |
- | appcount=${#app[*]} # 3 | + | echo ">$rein<" |
- | appix=1 | + | } |
- | echo ${app[appix]} # XINE | + | |
- | </code> | + | |
- | ===== Loops == | + | coproc test_coproc |
- | <code bash> | + | echo "Hallo" >&${COPROC[1]} |
- | while | + | read -u ${COPROC[0]} rein |
- | read -p 'Search term (English or German): ' inp | + | echo "$rein" |
- | test "$inp" | + | #cat <&${COPROC[0]} # alternative |
- | do | + | </code> |
- | query $inp | + | |
- | done | + | |
- | for (( ix=10 ; ix<21 ; ix++ )) ; do | + | ===== Expansion == |
- | echo $ix | + | |
- | done | + | |
- | for arg in "$@"; do # $@ sees arguments as separate words. | + | FIXME doc ''*'' and ''?'' |
- | echo $arg | + | |
- | done | + | |
- | # Print each word (separated by whitespace) in a textfile into one line (tested): | + | <code bash> |
- | for token in $(cat textfile); do | + | echo {a,}quota.{group,user}.new |
- | echo $token | + | # prints: aquota.group.new aquota.user.new quota.group.new quota.user.new |
- | done | + | # without any regard to the content of the working directory |
</code> | </code> | ||
===== Variables == | ===== Variables == | ||
+ | |||
$meli is a shortcut for ${meli} | $meli is a shortcut for ${meli} | ||
Zeile 83: | Zeile 89: | ||
$$ # Pid of script | $$ # Pid of script | ||
$? # return value of command (& function?) | $? # return value of command (& function?) | ||
+ | $! # PID of last job run in background | ||
$0 # full name of script | $0 # full name of script | ||
$1 # first argument of script | $1 # first argument of script | ||
Zeile 88: | Zeile 95: | ||
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]])). | 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: | ||
+ | |||
+ | <code bash> | ||
+ | ${variable:-defaultvalue} | ||
+ | |||
+ | # Example: | ||
+ | #Keep Value of VERSION or if not set yet, set it to 3.9: | ||
+ | VERSION=${VERSION:-3.9} | ||
+ | </code> | ||
+ | |||
+ | [[tldp>LDP/abs/html/parameter-substitution.html|Source]] | ||
+ | |||
+ | ===== Arrays == | ||
+ | |||
+ | <code bash> | ||
+ | #!/bin/bash | ||
+ | |||
+ | # first index is 0 | ||
+ | app=( XMMS XINE XAWTV ) | ||
+ | appcount=${#app[*]} # 3 | ||
+ | appix=1 | ||
+ | echo ${app[appix]} # XINE | ||
+ | </code> | ||
+ | |||
+ | ===== Maps == | ||
+ | |||
+ | See http://stackoverflow.com/questions/1494178/how-to-define-hash-tables-in-bash | ||
+ | |||
+ | ===== Loops == | ||
+ | |||
+ | <code bash> | ||
+ | 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 | ||
+ | </code> | ||
===== Quoting == | ===== Quoting == | ||
Zeile 108: | Zeile 187: | ||
===== Functions == | ===== Functions == | ||
+ | ==== Parameters == | ||
+ | FIXME | ||
<code bash> | <code bash> | ||
- | query() { | + | myfunc() { |
- | echo $*; echo $@; echo $1 | + | echo $* #-> eins zwei drei |
+ | echo $@ #-> eins zwei drei | ||
+ | echo $1 #-> eins | ||
} | } | ||
- | query # call the function | + | myfunc eins zwei drei # call the function |
</code> | </code> | ||
- | Example: | + | ==== 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: | ||
<code bash> | <code bash> | ||
- | abort() { | + | myfunc() { |
- | echo "ERROR: $1!" >&2 | + | echo "Hello $1" |
- | if [ "$2" ]; then errcode=$2 ; else errcode=1 ; fi | + | |
- | exit $errcode | + | |
} | } | ||
- | # usage: | + | |
- | [ -d "$SOURCE" ] || abort "Source \"$SOURCE\" is not a directory" | + | res=$(myfunc 'crazy world') # Oddly no quotes necessary in bash and ash |
+ | echo ">>$res<<" | ||
+ | </code> | ||
+ | |||
+ | By using a separator (tab) and the cut command it is possible to return more than one value: | ||
+ | |||
+ | <code bash> | ||
+ | myfunc() { | ||
+ | echo -e "EINS\tZWEI" | ||
+ | } | ||
+ | |||
+ | buf=$(myfunc) | ||
+ | eins=$(echo "$buf" | cut -f1) | ||
+ | zwei=$(echo "$buf" | cut -f2) | ||
+ | |||
+ | echo "$eins - $zwei" #-> EINS - ZWEI | ||
</code> | </code> | ||
Zeile 144: | Zeile 245: | ||
elif [ "$a" == "$b" ] && [ "c" != "d" ]; then # also || | elif [ "$a" == "$b" ] && [ "c" != "d" ]; then # also || | ||
... | ... | ||
- | elif [ ! "$aString" ]; then # true if length==0 or not specified (tested) | + | elif [ -z "$aString" ]; then # true if length==0 |
... | ... | ||
fi | fi | ||
Zeile 157: | Zeile 258: | ||
-h file True if file exists and is a symbolic link. | -h file True if file exists and is a symbolic link. | ||
-r file True if file exists and is readable. | -r file True if file exists and is readable. | ||
- | -s file True if file exists and has a size greater than zero. | + | -w file True if file exists and is writable. |
- | # see also "man bash" -> "Conditional expressions" | + | -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: | # Example: | ||
- | if [ ! -r "$sorcefile" ]; then | + | if [ ! -r "$sourcefile" ]; then |
echo "Error: Cant read $sorcefile!" | echo "Error: Cant read $sorcefile!" | ||
exit 2 | exit 2 | ||
Zeile 166: | Zeile 271: | ||
</code> | </code> | ||
- | ===== Redirection ===== | + | See also: [[man>test]] and [[man>bash]] -> "Conditional expressions" |
- | <code bash> | + | ===== Exit status of a command == |
- | 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) | + | |
- | </code> | + | |
- | + | ||
- | ===== Exit status of a command ===== | + | |
<code bash> | <code bash> | ||
Zeile 189: | Zeile 285: | ||
echo "command was successful" | echo "command was successful" | ||
fi | fi | ||
+ | </code> | ||
+ | |||
+ | ''abort'', my useful helper function: | ||
+ | <code bash> | ||
+ | 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" | ||
+ | </code> | ||
+ | |||
+ | ===== Redirection == | ||
+ | |||
+ | <code bash> | ||
+ | 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) | ||
</code> | </code> | ||