Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
|
becki:linux:bash [2011-03-30 07:56] 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 20: | Zeile 27: | ||
| 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 | ||
| + | |||
| + | ===== Subshells == | ||
| + | |||
| + | FIXME | ||
| + | |||
| + | ===== Coprocesses == | ||
| + | |||
| + | The following example starts a shell function as a coproc and uses the coproc to manipulate a string: | ||
| + | |||
| + | <code bash> | ||
| + | #!/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 | ||
| + | </code> | ||
| + | |||
| + | ===== Expansion == | ||
| + | |||
| + | FIXME doc ''*'' and ''?'' | ||
| + | |||
| + | <code bash> | ||
| + | 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 | ||
| + | </code> | ||
| + | |||
| + | ===== Variables == | ||
| + | |||
| + | |||
| + | $meli is a shortcut for ${meli} | ||
| + | |||
| + | ==== Variable Variables == | ||
| + | |||
| + | <code bash> | ||
| + | # 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 | ||
| + | </code> | ||
| + | |||
| + | ==== Special Variables == | ||
| + | |||
| + | <code bash> | ||
| + | $# # 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 | ||
| + | </code> | ||
| + | |||
| + | 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 == | ===== Arrays == | ||
| Zeile 32: | Zeile 121: | ||
| echo ${app[appix]} # XINE | echo ${app[appix]} # XINE | ||
| </code> | </code> | ||
| + | |||
| + | ===== Maps == | ||
| + | |||
| + | See http://stackoverflow.com/questions/1494178/how-to-define-hash-tables-in-bash | ||
| ===== Loops == | ===== Loops == | ||
| Zeile 49: | Zeile 142: | ||
| for arg in "$@"; do # $@ sees arguments as separate words. | for arg in "$@"; do # $@ sees arguments as separate words. | ||
| echo $arg | echo $arg | ||
| + | done | ||
| + | |||
| + | # Loop through all ogg files of current dir. This is whitepace-save | ||
| + | for fn in *.ogg; do | ||
| + | echo $fn | ||
| done | done | ||
| Zeile 69: | Zeile 167: | ||
| done | done | ||
| </code> | </code> | ||
| - | |||
| - | ===== Variables == | ||
| - | |||
| - | $meli is a shortcut for ${meli} | ||
| - | |||
| - | ==== Variable Variables == | ||
| - | |||
| - | <code bash> | ||
| - | # 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 | ||
| - | </code> | ||
| - | |||
| - | ==== Special Variables == | ||
| - | |||
| - | <code bash> | ||
| - | $# # number of command line arguments | ||
| - | $* # Arguments as one word | ||
| - | $@ # Arguments as separate words | ||
| - | $$ # Pid of script | ||
| - | $? # return value of command (& function?) | ||
| - | $0 # full name of script | ||
| - | $1 # first argument of script | ||
| - | </code> | ||
| - | |||
| - | 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]])). | ||
| ===== Quoting == | ===== Quoting == | ||
| Zeile 121: | 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 157: | 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 170: | 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 179: | 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 202: | 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> | ||