#lang en <> = Description = This is a reference for bash scripting. For more general instructions take a look [[Links/Bash | here]]. = Operators = == Usage == Bash operators are mainly used in if statements, to check against a variable or a hard coded object. Example: {{{#!highlight bash #!/bin/bash name=Foo if [ -e $name ] then echo File Exist else echo File doesnot exist fi }}} == File Test Operators == ||'''Operator'''||'''Checks for'''||'''Returns'''|| ||<:#FF9999>(`-a`) ||<#FF9999> File exits '''DEPRECATED''' ||<#FF9999> True if it exits, otherwise false || ||<:>`-b` || File is a block device || True if the file is a block device, otherwise false || ||<:>`-c` || File is a character device || True if it is a character device, otherwise false || ||<:>`-d` || File is a directory || True if it is a directory, otherwise false || ||<:>`-e` || File exits || True if it exits, otherwise false || ||<:>`-ef` || Files are hard links to the same file || True if files before and after operator are hard links to the same file, otherwise false || ||<:>`-f` || File is regular file || True if regular, false if directory, device file, or non existant || ||<:>`-g` || Set-group-id (sgid) flag set on directory<> || True if flag is set, otherwise false || ||<:>`-G` || Group-id of file is equal to the current user's || True group-id is equal to the user's, otherwise false || ||<:>`-h` || File is a symbolic link || True if file is a symbolic link, otherwise false || ||<:>`-k` || Sticky bit set<> || True if bit is set, otherwise false || ||<:>`-L` || File is a symbolic link || True if file is a symbolic link, otherwise false || ||<:>`-N` || File is modified since last read || True file is modified since last read, otherwise false || ||<:>`-nt` || File is newer than other file || True if file preceding operator is newer than file behind the operator, otherwise false || ||<:>`-ot` || File is older than other file || True if file preceding operator is older than file behind the operator, otherwise false || ||<:>`-O` || Current user is owner of file || True if user is owner, otherwise false || ||<:>`-p` || File is a pipe || True if file is a pipe, otherwise false || ||<:>`-r` || File has read permission (for the user running the test) || True if it has read access, otherwise false || ||<:>`-s` || File Size || True if file > 0, otherwise false || ||<:>`-S` || File is a socket || True if file is a socket, otherwise false || ||<:>`-t` || File (descriptor) is associated with a terminal device<> || True if file is associated with a terminal device, otherwise false || ||<:>`-u` || Set-user-id (suid) flag set on file<> || True if flag is set, otherwise false || ||<:>`-w` || File has write permission (for the user running the test) || True if it has write access, otherwise false || ||<:>`-x` || File has execute permission (for the user running the test) || True if it has execute access, otherwise false || ||<:>`!` || NOT, reverses the tests above || True if condition absent, otherwise reverse of condition || == Integer Comparison Operators == ||'''Operator'''||'''C-style variant'''||'''Meaning'''||'''Usage'''||'''Usage C-Style'''|| ||<:>`-eq` ||<:> none || Is equal to || `if [ "$a" -eq "$b" ]` || - || ||<:>`-ne` ||<:> none || Is not equal to || `if [ "$a" -ne "$b" ]` || - || ||<:>`-gt` ||<:> `>` || Is greater than || `if [ "$a" -gt "$b" ]` || `(("$a" > "$b"))` || ||<:>`-ge` ||<:> `>=` || Is greater or equal to || `if [ "$a" -ge "$b" ]` || `(("$a" >= "$b"))` || ||<:>`-lt` ||<:> `<` || Is less than || `if [ "$a" -lt "$b" ]` || `(("$a" < "$b"))` || ||<:>`-le` ||<:> `<=` || Is less than or equal to || `if [ "$a" -le "$b" ]` || `(("$a" <= "$b"))` || == String Comparison Operators == ||'''Operator'''||'''Meaning'''||'''Usage'''|| ||<:>`=` || Is equal to || `if [ "$a" = "$b" ]` || ||<:>`==` || Is equal to<> || `if [ "$a" == "$b" ]` || ||<:>`!=` || Is not equal to || `if [ "$a" != "$b" ]` || ||<:>`=~` || Check string against extended regex || `[[ $a =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]`|| ||<:>`<` || Is less than, in ASCII alphabetical order || `if [ "$a" \< "$b" ]` or `if [[ "$a" < "$b" ]]` || ||<:>`>` || Is greater than, in ASCII alphabetical order || `if [ "$a" \> "$b" ]` or `if [[ "$a" > "$b" ]]` || ||<:>`-z` || string is null(zero length) || `if [ -z "$String" ]` || ||<:>`-n` || string is not null || `if [ -n "$String" ]` (always use quotes) || == Arithmetic Operators == ||'''Operator'''||'''Name'''||'''Funtion'''|| ||<:>`+` || Plus || 3 + 6 = 6 || ||<:>`-` || Minus || 6 - 3 = 3 || ||<:>`*` || Multiply || 3 * 3 = 9 || ||<:>`/` || Divide || 9 / 3 = 3 || ||<:>`**` || Exponent || 3 ** 2 = 9 || ||<:>`%` || Modulo || 24 % 4 = 4 || Each arithmetic operator can be followed with an equals sign(=), which writes the output of the operation into the first variable. An example: {{{#!highlight bash #!/bin/bash a=2 $((a += 1)) echo $a # returns: 3 }}} == Logical Comparison Operators == ||'''Operator'''||'''Name'''||'''Funtion'''|| ||<:>`&&` || AND || "true" && "false" = false || ||<:>`||` || OR || "true" `||` "false" = true || ||<:>`!` || NOT || ! "true" = false || Usage: {{{#!highlight bash #!/bin/bash if [ $condition1 ] && [ $condition2 ]; ... if [[ $condition1 && $condition2 ]]; ... if [ ! -f $FILENAME ]; ... }}} == Compound Comparison Operators == ||'''Operator'''||'''Name'''||'''Usage'''|| ||<:>`-a` || AND || if [ "$expr1" -a "$expr2"]<> || ||<:>`-o` || OR || if [ "$expr1" -o "$expr2"]<> || == Bitwise Comparison Operators == ||'''Operator'''||'''Name'''||'''Funtion'''|| ||<:>`&` || AND || 1010 & 1100 = 1000 || ||<:>`|` || OR || 1010 | 1100 = 1110 || ||<:>`^` || XOR || 1010 ^ 1100 = 0110 || ||<:>`~` || NOT/Complement || ~1010 = 0101 || ||<:>`<<` || Left Shift || 1100 << 1 = 1000|| ||<:>`>>` || Right Shift || 1100 >> 1 = 0110|| Bitwise operators are usually not used in if statements, but for number manipulation. An example: {{{#!highlight bash #!/bin/bash a=$((2#1010)) b=12 bitwiseAND=$(( a&b )) echo Bitwise AND of a and b is $bitwiseAND # returns: Bitwise AND of a and b is 8 }}} Each bitwise operator can be followed with an equals sign(=), which writes the output of the operation into the first variable. An example: {{{#!highlight bash #!/bin/bash a=2 $((a <<= 1)) echo $a # returns: 4 }}} = Syntax = == Functions == A function in bash can be defined in 2 ways: {{{#!highlight bash function_name () { } }}} or {{{#!highlight bash function function_name () { } }}} The `()` are there just for decoration, not to define arguments. Don't put anything between these. Arguments can be accessed using `$1` etc. Arguments passed from the commandline can't be accessed directly from within a function. You should pass these when calling the function, or expose them in a global variable. === Calling === Calling functions is done by calling `[result = ]function_name 'argument1' 'argument2'`. Don't use parentheses for passing arguments. == Parameter Substitution and Expansion == [[ https://tldp.org/LDP/abs/html/refcards.html#PARSUBTAB | see the tldp refcard ]] [[ https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html | More about parameter expansion ]] === Case modification === (new in Bash 4.0) ||'''Operator'''||'''Funtion'''|| ||<:>`^^` || To uppercase || ||<:>`^` || First (matched) character to uppercase || ||<:>`,,` || To lowercase || ||<:>`,` || First (matched) character to lowercase || Usage: {{{#!highlight bash var='Text' echo "${var^^}" > TEXT }}} This can be used with a pattern like this: `${var^pattern}`. In which case only matched characters are transformed === Parameter transformation === [[ https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html | Check the bottom of this page ]] ||'''Operator'''||'''Funtion'''|| ||<:>`U` || To uppercase (new in Bash 5.1) || ||<:>`u` || First character to uppercase (new in Bash 5.1) || ||<:>`L` || To lowercase (new in Bash 5.1) || ||<:>`Q` || Quote/escape || ||<:>`E` || With backslash escape sequences expanded as with the $'…' quoting mechanism || ||<:>`P` || Expand as if it were a prompt string || ||<:>`A` || As assignment statement || ||<:>`K` || Display associative arrays as key-value pairs (new in Bash 5.1) || ||<:>`a` || Show parameters' attributes flags [[https://linuxcommand.org/lc3_man_pages/declareh.html|parameters can have flags!]] || Usage: {{{#!highlight bash var='Text' echo "${var@U}" > TEXT }}} == String Operations == [[ https://tldp.org/LDP/abs/html/refcards.html#STRINGOPSTAB | see the tldp refcard ]] = Loops = == For loop == Iterate over all passed files {{{#!highlight bash #!/bin/bash for FILE1 in "$@" do wc $FILE1 done }}} Iterate over all lines in variable {{{#!highlight bash while IFS= read -r line; do echo "... $line ..." done <<< "$list" }}} == While loop == Loop until condition is true {{{#!highlight bash while [ $i -lt 4 ] do echo hi & i=$[$i+1] done }}} = Special variables = == Positional variables == Positional variables can be used within functions or scripts, enumerating arguments passed to them. {{{#!highlight bash function positional_variables(){ echo "Positional variable 1: ${1}" echo "Positional variable 2: ${2}" } $ positional_variables "one" "two" Positional variable 1: one Positional variable 2: two }}} Using `${@}` or `${@}` all positional arguments can be returned, as iterable or as expanded array. The difference is that `${@}` expands the variables like this `${1} ${2}..` and `${@}` uses `${1}c${2}..` where c is the first character set in IFS. == Script source dir == Sometimes you need the directory in which the script itself is located, for example if you need to copy/make files in that directory. There are a lot of ways to get this directory, however, not all methods still work when the script is called via a symlink. The following line should work in most cases: {{{#!highlight bash script_path="$(readlink -f "${BASH_SOURCE[0]}")" script_dir="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" }}} A test to check this method can be found [[https://gist.github.com/tvlooy/cbfbdb111a4ebad8b93e|here]] = Executing commands = With executing commands, I mean running a program/command from within a bash script. For example: {{{#!highlight bash #!/bin/bash touch "testfile" }}} == Piping output == === Piping to /dev/null === When a command should not display any output to the commandline, it is possible to pipe both STDOUT and STRERR to /dev/null {{{#!highlight bash echo "this won't be shown" >/dev/null 2>&1 }}} = Snippets = Useful pieces of code == Bash script hardening == [[ https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425 | Explanation -euxo pipefail ]] [[ https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ | Safer bashscripts with euxo pipefail + E ]] {{{#!highlight bash #! /bin/bash set -Eeuxo pipefail }}} === Resolving unbound variable error === When using `set -u`, the processing of undefined commandline arguments might throw an error like this: {{{#!highlight bash ./script.sh: line 24: $1: unbound variable }}} To work around this error, you can use parameter substitution to replace an undefined parameter with an empty string like this: {{{#!highlight bash [ -z "${1-}" ] || [ "$1" == "help" ] && help }}} == Check if file with wildcard exists == The following will echo true when a file called test.* exists {{{#!highlight bash compgen -G "test.*" && echo true }}} == Yes/No prompt == {{{#!highlight bash #!/bin/bash read -p "Do you wish to be evil? (Y/n) " use_systemd [ -z "$use_systemd" ] && use_systemd='yes' # if no input, assume yes case ${use_systemd:0:1} in y|Y|1 ) # Yes echo "Try https://www.google.com";; * ) # No (anything else) echo "Better go to https://duckduckgo.com";; esac }}} == Check if running with sudo == {{{#!highlight bash #!/bin/bash if [[ $EUID -ne 0 ]]; then echo "This script must be run as root" exit 1 fi }}} == Commandline parameter parser (getopts) == {{{#!highlight bash #!/bin/bash function help () { echo "Usage: $0 [OPTIONS] " echo "Description of what this command does" echo " -a what option a does" echo " -b what option b does" echo " -c what input c does" echo " -h display this output" exit 1 } while getopts ':abc:h' opt ; do case "$opt" in a) a_var='true';; b) b_var='true';; c) c_var="${OPTARG}";; h) help ;; :) echo "$0: Must supply an argument to -$OPTARG." >&2 exit 1 ;; ?) echo "Invalid option: -${OPTARG}." exit 2 ;; esac done file=${@:$OPTIND:1} output_file=${@:($OPTIND +1):1} }}} * `${@:$OPTIND:1}` gets the first argument after the options parsed by getopts * `${@:($OPTIND +1):1}` gets the second, `${@:($OPTIND +2):1}` the third etc. == Lockfile & Trap == This snippet demonstrates both how a lockfile can be used, as well as how to trap any errors, so the lockfile can be removed. As long as the trap uses `exit` without an exit code, the exit code that was trapped will be returned. {{{#!highlight bash #!/bin/bash DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" LOCKFILE="$DIR/cronjob.lock" set -e # Get lockfile counter=0 while [ -f "$LOCKFILE" ]; do sleep 1 if [ "$counter" -ge 5 ]; then echo "Could not get hold of lockfile, aborting." exit 1 fi counter=$((counter+1)) done trap "{ echo Bye!; rm -f "$LOCKFILE"; exit; }" EXIT echo $$ > "$LOCKFILE" # Do something that can't be multithreaded exit 0 }}} == Convert UNIX file with CR line endings to DOS file with CR/LF line endings == {{{#!highlight bash #!/bin/bash cat normalfile.txt | sed 's/$'"/`echo \\\r`/" > inferiorfile.txt }}} == Iterate over find output (or lines in file) == {{{#!highlight bash while IFS= read -r -d '' file; do echo "$file"; done < <(find . -name '*PATTERN*' -print0) # And rename each file while IFS= read -r -d '' file; do mv -v "$file" "$(echo "$file" | sed 's/.txt/.conf/')"; done < <(find . -name '*PATTERN*' -print0) }}} = Footnotes =