Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
|
becki:linux:bash [2008-09-30 14:03] 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 | ||
| + | </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 == | ||
| + | |||
| + | <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> | </code> | ||
| Zeile 104: | Zeile 185: | ||
| * Backslashes are always interpreted | * Backslashes are always interpreted | ||
| * Variables are ony substituted between double quotes | * Variables are ony substituted between double quotes | ||
| - | |||
| ===== Functions == | ===== Functions == | ||
| + | ==== Parameters == | ||
| + | FIXME | ||
| <code bash> | <code bash> | ||
| - | function 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> | ||
| - | function 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 143: | 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 156: | 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 165: | 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 188: | 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> | ||