diff --git a/_config.yml b/_config.yml index 84b497860..0c69df5f2 100644 --- a/_config.yml +++ b/_config.yml @@ -3,6 +3,10 @@ whitelist: - jekyll-redirect-from - jekyll-github-metadata +plugins: + - jekyll-redirect-from + - jekyll-github-metadata + # files exclude: - Makefile diff --git a/_layouts/redirect.html b/_layouts/redirect.html new file mode 100644 index 000000000..145bc6d73 --- /dev/null +++ b/_layouts/redirect.html @@ -0,0 +1,10 @@ + + + + Redirecting… + + +

Redirecting...

+ Click here if you are not redirected. + + diff --git a/bash.md b/bash.md index c6de4d2ba..c2336c771 100644 --- a/bash.md +++ b/bash.md @@ -1,5 +1,578 @@ --- title: Bash scripting category: CLI -redirect_to: /sh +layout: 2017/sheet +tags: [Featured] +redirect_from: + - /sh + - /sh.html --- + +Getting started +--------------- +{: .-three-column} + +### Example + +```bash +#!/usr/bin/env bash + +NAME="John" +echo "Hello $NAME!" +``` + +### Variables + +```bash +NAME="John" +echo $NAME +echo "$NAME" +echo "${NAME}!" +``` + +### String quotes + +```bash +NAME="John" +echo "Hi $NAME" #=> Hi John +echo 'Hi $NAME' #=> Hi $NAME +``` + +### Shell execution + +```bash +echo "I'm in $(pwd)" +echo "I'm in `pwd`" +# Same +``` + +See [Command substitution](http://wiki.bash-hackers.org/syntax/expansion/cmdsubst) + +### Conditional execution + +```bash +git commit && git push +git commit || echo "Commit failed" +``` + +### Functions +{: id='functions-example'} + +```bash +get_name() { + echo "John" +} + +echo "You are $(get_name)" +``` + +See: [Functions](#functions) + +### Conditionals +{: id='conditionals-example'} + +```bash +if [ -z "$string" ]; then + echo "String is empty" +elsif [ -n "$string" ]; then + echo "String is not empty" +fi +``` + +See: [Conditionals](#conditionals) + +### Strict mode + +```bash +set -euo pipefail +IFS=$'\n\t' +``` + +See: [Unofficial bash strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/) + +### Brace expansion + +```bash +echo {A,B}.js +``` + +| `{A,B}` | Same as `A B` | +| `{A,B}.js` | Same as `A.js B.js` | +| `{1..5}` | Same as `1 2 3 4 5` | + +See: [Brace expansion](http://wiki.bash-hackers.org/syntax/expansion/brace) + + +Parameter expansions +-------------------- +{: .-three-column} + +### Basics + +```bash +name="John" +echo ${name} +echo ${name/J/j} #=> "john" (substitution) +echo ${name:0:2} #=> "jo" (slicing) +echo ${food:-Cake} #=> $food or "Cake" +``` + +```bash +length=2 +echo ${name:0:length} #=> "jo" +``` + +See: [Parameter expansion](http://wiki.bash-hackers.org/syntax/pe) + +```bash +STR="/path/to/foo.cpp" +echo ${STR%.cpp} # /path/to/foo +echo ${STR%.cpp}.o # /path/to/foo.o + +echo ${STR##*.} # cpp (extension) +echo ${STR##*/} # foo.cpp (basepath) + +echo ${STR#*/} # path/to/foo.cpp +echo ${STR##*/} # foo.cpp + +echo ${STR/foo/bar} # /path/to/bar.cpp +``` + +```bash +STR="Hello world" +echo ${STR:6:5} # "world" +echo ${STR:-5:5} # "world" +``` + +```bash +SRC="/path/to/foo.cpp" +BASE=${STR##*/} #=> "foo.cpp" (basepath) +DIR=${SRC%$BASE} #=> "/path/to" (dirpath) +``` + +### Substitution + +| Code | Description | +| --- | --- | +| `${FOO%suffix}` | Remove suffix | +| `${FOO#prefix}` | Remove prefix | +| --- | --- | +| `${FOO%%suffix}` | Remove long suffix | +| `${FOO##prefix}` | Remove long prefix | +| --- | --- | +| `${FOO/from/to}` | Replace first match | +| `${FOO//from/to}` | Replace all | +| --- | --- | +| `${FOO/%from/to}` | Replace suffix | +| `${FOO/#from/to}` | Replace prefix | + +### Substrings + +| `${FOO:0:3}` | Substring _(position, length)_ | +| `${FOO:-3:3}` | Substring from the right | + +### Length + +| `${#FOO}` | Length of `$FOO` | + +### Default values + +| `${FOO:-val}` | `$FOO`, or `val` if not set | +| `${FOO:=val}` | Set `$FOO` to `val` if not set | +| `${FOO:+val}` | `val` if `$FOO` is set | +| `${FOO:?message}` | Show error message ande xit if `$FOO` is not set | + +The `:` is optional (eg, `${FOO=word}` works) + +Loops +----- +{: .-three-column} + +### Basic for loop + +```bash +for i in /etc/rc.*; do + echo $i +done +``` + +### Ranges + +```bash +for i in {1..5}; do + echo "Welcome $i" +done +``` + +### Reading lines + +```bash +cat file.txt | while read line; do + echo $line +done +``` + +### Forever + +```bash +while true; do + ··· +done +``` + +Functions +--------- +{: .-three-column} + +### Defining functions + +```bash +myfunc() { + echo "hello $1" +} +``` + +```bash +# Same as above (alternate syntax) +function myfunc() { + echo "hello $1" +} +``` + +```bash +myfunc "John" +``` + +### Returning values + +```bash +myfunc() { + local myresult='some value' + echo $myresult +} +``` + +```bash +result=$(myfunc) +``` + +### Raising errors + +```bash +myfunc() { + return 1 +} +``` + +```bash +if myfunc; then + echo "success" +else + echo "failure" +fi +``` + +### Arguments + +| Expression | Description | +| --- | --- | +| `$#` | Number of arguments | +| `$*` | All arguments | +| `$@` | All arguments, starting from first | +| `$1` | First argument | + +See [Special parameters](http://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables). + +Conditionals +------------ +{: .-three-column} + +### Conditions + +| Condition | Description | +| --- | --- | +| `[ -z STRING ]` | Empty string | +| `[ -n STRING ]` | Not empty string | +| --- | --- | +| `[ NUM -eq NUM ]` | Equal | +| `[ NUM -ne NUM ]` | Not equal | +| `[ NUM -lt NUM ]` | Less than | +| `[ NUM -le NUM ]` | Less than or equal | +| `[ NUM -gt NUM ]` | Greater than | +| `[ NUM -ge NUM ]` | Greater than or equal | +| --- | --- | +| `[[ STRING =~ STRING ]]` | Regexp | +| --- | --- | +| `(( NUM < NUM ))` | Numeric conditions | + +| Condition | Description | +| --- | --- | +| `[ -o noclobber ]` | If OPTIONNAME is enabled | +| --- | --- | +| `[ ! EXPR ]` | Not | +| `[ X ] && [ Y ]` | And | +| `[ X ] || [ Y ]` | Or | + +### File conditions + +| Condition | Description | +| --- | --- | +| `[ -e FILE ]` | Exists | +| `[ -r FILE ]` | Readable | +| `[ -h FILE ]` | Symlink | +| `[ -d FILE ]` | Directory | +| `[ -w FILE ]` | Writable | +| `[ -s FILE ]` | Size is > 0 bytes | +| `[ -f FILE ]` | File | +| `[ -x FILE ]` | Executable | +| --- | --- | +| `[ FILE1 -nt FILE2 ]` | 1 is more recent than 2 | +| `[ FILE1 -ot FILE2 ]` | 2 is more recent than 1 | +| `[ FILE1 -ef FILE2 ]` | Same files | + +### Example + +```bash +# String +if [ -z "$string" ]; then + echo "String is empty" +elsif [ -n "$string" ]; then + echo "String is not empty" +fi +``` + +```bash +# Combinations +if [ X ] && [ Y ]; then + ... +fi +``` + +```bash +# Regex +if [[ "A" =~ "." ]] +``` + +```bash +if (( $a < $b )) +``` + +```bash +if [ -e "file.txt" ]; then + echo "file exists" +fi +``` + +Arrays +------ + +### Defining arrays + +```bash +Fruits=('Apple' 'Banana' 'Orange') +``` + +```bash +Fruits[0]="Apple" +Fruits[1]="Banana" +Fruits[2]="Orange" +``` + +### Working with arrays + +```bash +echo ${Fruits[0]} # Element #0 +echo ${Fruits[@]} # All elements, space-separated +echo ${#Fruits[@]} # Number of elements +echo ${#Fruits} # String length of the 1st element +echo ${#Fruits[3]} # String length of the Nth element +echo ${Fruits[@]:3:2} # Range (from position 3, length 2) +``` + +### Operations + +```bash +Fruits=("${Fruits[@]}" "Watermelon") # Push +Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match +unset Fruits[2] # Remove one item +Fruits=("${Fruits[@]}") # Duplicate +Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate +lines=(`cat "logfile"`) # Read from file +``` + +### Iteration + +```bash +for i in "${arrayName[@]}"; do + echo $i +done +``` + +Options +------- + +### Options + +```bash +set -o noclobber # Avoid overlay files (echo "hi" > foo) +set -o errexit # Used to exit upon error, avoiding cascading errors +set -o pipefail # Unveils hidden failures +set -o nounset # Exposes unset variables +``` + +### Glob options + +```bash +set -o nullglob # Non-matching globs are removed ('*.foo' => '') +set -o failglob # Non-matching globs throw errors +set -o nocaseglob # Case insensitive globs +set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh") +set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb') +``` + +Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob +matches. + +Miscellaneous +------------- + +### Numeric calculations + +```bash +$((a + 200)) # Add 200 to $a +``` + +```bash +$((RANDOM%=200)) # Random number 0..200 +``` + +### Subshells + +```bash +(cd somedir; echo "I'm now in $PWD") +pwd # still in first directory +``` + +### Redirection + +```bash +python hello.py > output.txt # stdout to (file) +python hello.py >> output.txt # stdout to (file), append +python hello.py 2> error.log # stderr to (file) +python hello.py 2>&1 # stderr to stdout +python hello.py 2>/dev/null # stderr to (null) +``` + +```bash +python hello.py < foo.txt +``` + +### Inspecting commands + +```bash +command -V cd +#=> "cd is a function/alias/whatever" +``` + +### Trap errors + +```bash +trap 'echo Error at about $LINENO' ERR +``` + +or + +```bash +traperr() { + echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}" +} + +set -o errtrace +trap traperr ERR +``` + +### Case/switch + +```bash +case "$1" in + start | up) + vagrant up + ;; + + *) + echo "Usage: $0 {start|stop|ssh}" + ;; +esac +``` + +### Source relative + +```bash +source "${0%/*}/../share/foo.sh" +``` + +### printf + +```bash +printf "Hello %s, I'm %s" Sven Olga +#=> "Hello Sven, I'm Olga +``` + +### Directory of script + +```bash +DIR="${0%/*}" +``` + +### Getting options + +```bash +while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in + -V | --version ) + echo $version + exit + ;; + -s | --string ) + shift; string=$1 + ;; + -f | --flag ) + flag=1 + ;; +esac; shift; done +if [[ "$1" == '--' ]]; then shift; fi +``` + +### Heredoc + +```sh +cat < Hi John -echo 'Hi $NAME' #=> Hi $NAME -``` - -### Shell execution - -```bash -echo "I'm in $(pwd)" -echo "I'm in `pwd`" -# Same -``` - -See [Command substitution](http://wiki.bash-hackers.org/syntax/expansion/cmdsubst) - -### Conditional execution - -```bash -git commit && git push -git commit || echo "Commit failed" -``` - -### Functions -{: id='functions-example'} - -```bash -get_name() { - echo "John" -} - -echo "You are $(get_name)" -``` - -See: [Functions](#functions) - -### Conditionals -{: id='conditionals-example'} - -```bash -if [ -z "$string" ]; then - echo "String is empty" -elsif [ -n "$string" ]; then - echo "String is not empty" -fi -``` - -See: [Conditionals](#conditionals) - -### Strict mode - -```bash -set -euo pipefail -IFS=$'\n\t' -``` - -See: [Unofficial bash strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/) - -### Brace expansion - -```bash -echo {A,B}.js -``` - -| `{A,B}` | Same as `A B` | -| `{A,B}.js` | Same as `A.js B.js` | -| `{1..5}` | Same as `1 2 3 4 5` | - -See: [Brace expansion](http://wiki.bash-hackers.org/syntax/expansion/brace) - - -Parameter expansions --------------------- -{: .-three-column} - -### Basics - -```bash -name="John" -echo ${name} -echo ${name/J/j} #=> "john" (substitution) -echo ${name:0:2} #=> "jo" (slicing) -echo ${food:-Cake} #=> $food or "Cake" -``` - -```bash -length=2 -echo ${name:0:length} #=> "jo" -``` - -See: [Parameter expansion](http://wiki.bash-hackers.org/syntax/pe) - -```bash -STR="/path/to/foo.cpp" -echo ${STR%.cpp} # /path/to/foo -echo ${STR%.cpp}.o # /path/to/foo.o - -echo ${STR##*.} # cpp (extension) -echo ${STR##*/} # foo.cpp (basepath) - -echo ${STR#*/} # path/to/foo.cpp -echo ${STR##*/} # foo.cpp - -echo ${STR/foo/bar} # /path/to/bar.cpp -``` - -```bash -STR="Hello world" -echo ${STR:6:5} # "world" -echo ${STR:-5:5} # "world" -``` - -```bash -SRC="/path/to/foo.cpp" -BASE=${STR##*/} #=> "foo.cpp" (basepath) -DIR=${SRC%$BASE} #=> "/path/to" (dirpath) -``` - -### Substitution - -| Code | Description | -| --- | --- | -| `${FOO%suffix}` | Remove suffix | -| `${FOO#prefix}` | Remove prefix | -| --- | --- | -| `${FOO%%suffix}` | Remove long suffix | -| `${FOO##prefix}` | Remove long prefix | -| --- | --- | -| `${FOO/from/to}` | Replace first match | -| `${FOO//from/to}` | Replace all | -| --- | --- | -| `${FOO/%from/to}` | Replace suffix | -| `${FOO/#from/to}` | Replace prefix | - -### Substrings - -| `${FOO:0:3}` | Substring _(position, length)_ | -| `${FOO:-3:3}` | Substring from the right | - -### Length - -| `${#FOO}` | Length of `$FOO` | - -### Default values - -| `${FOO:-val}` | `$FOO`, or `val` if not set | -| `${FOO:=val}` | Set `$FOO` to `val` if not set | -| `${FOO:+val}` | `val` if `$FOO` is set | -| `${FOO:?message}` | Show error message ande xit if `$FOO` is not set | - -The `:` is optional (eg, `${FOO=word}` works) - -Loops ------ -{: .-three-column} - -### Basic for loop - -```bash -for i in /etc/rc.*; do - echo $i -done -``` - -### Ranges - -```bash -for i in {1..5}; do - echo "Welcome $i" -done -``` - -### Reading lines - -```bash -cat file.txt | while read line; do - echo $line -done -``` - -### Forever - -```bash -while true; do - ··· -done -``` - -Functions ---------- -{: .-three-column} - -### Defining functions - -```bash -myfunc() { - echo "hello $1" -} -``` - -```bash -# Same as above (alternate syntax) -function myfunc() { - echo "hello $1" -} -``` - -```bash -myfunc "John" -``` - -### Returning values - -```bash -myfunc() { - local myresult='some value' - echo $myresult -} -``` - -```bash -result=$(myfunc) -``` - -### Raising errors - -```bash -myfunc() { - return 1 -} -``` - -```bash -if myfunc; then - echo "success" -else - echo "failure" -fi -``` - -### Arguments - -| Expression | Description | -| --- | --- | -| `$#` | Number of arguments | -| `$*` | All arguments | -| `$@` | All arguments, starting from first | -| `$1` | First argument | - -See [Special parameters](http://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables). - -Conditionals ------------- -{: .-three-column} - -### Conditions - -| Condition | Description | -| --- | --- | -| `[ -z STRING ]` | Empty string | -| `[ -n STRING ]` | Not empty string | -| --- | --- | -| `[ NUM -eq NUM ]` | Equal | -| `[ NUM -ne NUM ]` | Not equal | -| `[ NUM -lt NUM ]` | Less than | -| `[ NUM -le NUM ]` | Less than or equal | -| `[ NUM -gt NUM ]` | Greater than | -| `[ NUM -ge NUM ]` | Greater than or equal | -| --- | --- | -| `[[ STRING =~ STRING ]]` | Regexp | -| --- | --- | -| `(( NUM < NUM ))` | Numeric conditions | - -| Condition | Description | -| --- | --- | -| `[ -o noclobber ]` | If OPTIONNAME is enabled | -| --- | --- | -| `[ ! EXPR ]` | Not | -| `[ X ] && [ Y ]` | And | -| `[ X ] || [ Y ]` | Or | - -### File conditions - -| Condition | Description | -| --- | --- | -| `[ -e FILE ]` | Exists | -| `[ -r FILE ]` | Readable | -| `[ -h FILE ]` | Symlink | -| `[ -d FILE ]` | Directory | -| `[ -w FILE ]` | Writable | -| `[ -s FILE ]` | Size is > 0 bytes | -| `[ -f FILE ]` | File | -| `[ -x FILE ]` | Executable | -| --- | --- | -| `[ FILE1 -nt FILE2 ]` | 1 is more recent than 2 | -| `[ FILE1 -ot FILE2 ]` | 2 is more recent than 1 | -| `[ FILE1 -ef FILE2 ]` | Same files | - -### Example - -```bash -# String -if [ -z "$string" ]; then - echo "String is empty" -elsif [ -n "$string" ]; then - echo "String is not empty" -fi -``` - -```bash -# Combinations -if [ X ] && [ Y ]; then - ... -fi -``` - -```bash -# Regex -if [[ "A" =~ "." ]] -``` - -```bash -if (( $a < $b )) -``` - -```bash -if [ -e "file.txt" ]; then - echo "file exists" -fi -``` - -Arrays ------- - -### Defining arrays - -```bash -Fruits=('Apple' 'Banana' 'Orange') -``` - -```bash -Fruits[0]="Apple" -Fruits[1]="Banana" -Fruits[2]="Orange" -``` - -### Working with arrays - -```bash -echo ${Fruits[0]} # Element #0 -echo ${Fruits[@]} # All elements, space-separated -echo ${#Fruits[@]} # Number of elements -echo ${#Fruits} # String length of the 1st element -echo ${#Fruits[3]} # String length of the Nth element -echo ${Fruits[@]:3:2} # Range (from position 3, length 2) -``` - -### Operations - -```bash -Fruits=("${Fruits[@]}" "Watermelon") # Push -Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match -unset Fruits[2] # Remove one item -Fruits=("${Fruits[@]}") # Duplicate -Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate -lines=(`cat "logfile"`) # Read from file -``` - -### Iteration - -```bash -for i in "${arrayName[@]}"; do - echo $i -done -``` - -Options -------- - -### Options - -```bash -set -o noclobber # Avoid overlay files (echo "hi" > foo) -set -o errexit # Used to exit upon error, avoiding cascading errors -set -o pipefail # Unveils hidden failures -set -o nounset # Exposes unset variables -``` - -### Glob options - -```bash -set -o nullglob # Non-matching globs are removed ('*.foo' => '') -set -o failglob # Non-matching globs throw errors -set -o nocaseglob # Case insensitive globs -set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh") -set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb') -``` - -Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob -matches. - -Miscellaneous -------------- - -### Numeric calculations - -```bash -$((a + 200)) # Add 200 to $a -``` - -```bash -$((RANDOM%=200)) # Random number 0..200 -``` - -### Subshells - -```bash -(cd somedir; echo "I'm now in $PWD") -pwd # still in first directory -``` - -### Redirection - -```bash -python hello.py > output.txt # stdout to (file) -python hello.py >> output.txt # stdout to (file), append -python hello.py 2> error.log # stderr to (file) -python hello.py 2>&1 # stderr to stdout -python hello.py 2>/dev/null # stderr to (null) -``` - -```bash -python hello.py < foo.txt -``` - -### Inspecting commands - -```bash -command -V cd -#=> "cd is a function/alias/whatever" -``` - -### Trap errors - -```bash -trap 'echo Error at about $LINENO' ERR -``` - -or - -```bash -traperr() { - echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}" -} - -set -o errtrace -trap traperr ERR -``` - -### Case/switch - -```bash -case "$1" in - start | up) - vagrant up - ;; - - *) - echo "Usage: $0 {start|stop|ssh}" - ;; -esac -``` - -### Source relative - -```bash -source "${0%/*}/../share/foo.sh" -``` - -### printf - -```bash -printf "Hello %s, I'm %s" Sven Olga -#=> "Hello Sven, I'm Olga -``` - -### Directory of script - -```bash -DIR="${0%/*}" -``` - -### Getting options - -```bash -while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in - -V | --version ) - echo $version - exit - ;; - -s | --string ) - shift; string=$1 - ;; - -f | --flag ) - flag=1 - ;; -esac; shift; done -if [[ "$1" == '--' ]]; then shift; fi -``` - -### Heredoc - -```sh -cat <