Scripts are used to automate tasks and provide human-readable and portable way to do things.
Basics
It's expected that scripts will have .sh extension and executable bit set. You can check it using:
if [[ -f path && -x path ]]; then echo executable; fi
Setting it can be done using:
chmod +x path
Scripts without executable bit set can still be run from SystemD services or by passing path to it to bash or other shell.
First line of script should start with shebang (#! character sentence) followed by command that should be used to determine what program should be used to execute script. For bash you would
expect first line of script to be #!/usr/bin/env
bash
Variant used in past - #!/bin/bash - is not
always present or bash.
Double quote symbol (") can be used to create string of characters, potentially with values of variables or command outputs.
Single quote symbol (') can be used to create string of characters.
Streams
Script can redirect of command from one command directly to another. Typically it's recommended to use streams instead of temporary files (this allows skipping disk write then read).| |
pipe standard output of previous command to next one |
> |
redirects standard output to specified path (overwrites content) |
>> |
redirects standard output to specified path (appends to file's content) |
< |
takes standard input from specified file/command |
2>&1 |
redirects standard error to where standard output goes - portable, recommended method of processing standard error |
2>/dev/null |
redirects standard error to special null device (to discard it) |
&> |
redirects standard output and error to specified path (overwrites content) - non portable,not recommended |
&>> |
redirects standard output and error to specified path (appends to file's content) - not portable, not recommended |
Loops
Loops can be used to perform work multiple times. One can loop over arrays or ad hoc collections Ad hoc example:for i in {1..3}{a,b} 4 "5 6";do echo "${i}";done
output:
1a
1b
2a
2b
3a
3b
4
5 6
Example of looping over file lines rather than words:
while IFS= read -r line;do echo $line;done < <(cat file)
output:
1
2 3
Setup for example above:
echo '1
2 3' > file
Functions
Functions can be used to execute desired code multiple time, with different parameters (or not)function demo() {
echo "first: ${1}, second: ${2}"
}
demo
demo 1 2 3
demo "1 2"output:
first: , second:
first: 1, second: 2
first: 1 2, second:
Variables
Variables can be used to store values for later useDeclaring variables
declare foo="item1 item2 item3"
Alternative
foo="item1 item2 item3"
Checking state of variable foo
if [ ! -v foo ]
then
echo "Variable foo was not set"
elif [ -z "$foo" ]
then
echo "Variable foo is set to an empty string"
else
echo "Variable foo is set"
fi
Using variables
echo "${foo}"
Passing to function (example read_var displays value of variable)
function read_var() {
for i in "$@"
do
echo "$i"
done
}
declare foo=("item1" "item2" "item3")
read_var "${foo}"
Arrays
Declaring arrays
declare -a array_var=("item1" "item2" "item3")
Alternative
array_var=("item1" "item2" "item3")
Checking size of array
echo "${#array_var[@]}"
Using arrays
-
Accessing item under specific index (in this example, second item - first item is under index 0)
echo "${array_var[1]}" -
Safely iterating over elements of arrays (double quotes around variable name block are not optional -
without them, for will split on whitespaces in array elements)
One-liner:for i in "${array_var[@]}" do echo "$i" donefor i in "${array_var[@]}"; do echo "$i"; done -
Passing arrays to function (example iterate_over_array iterates over array elements)
function iterate_over_array() { for i in "$@" do echo "$i" done } declare -a array_var=("item1" "item2" "item3") iterate_over_array "${array_var[@]}"
Conditional statements
Allows matching actions to conditionGeneral syntax
if condition1
then
statements1
elif condition2
then
statements2
else
fallbackStatements
fi
Single line syntax:
if condition1; then statements1; elif condition2; then
statements2; else fallbackStatements; fi
Examples
if [[ $foo > 123 ]]
then
echo "$foo is more than 123"
elif [[ $foo = 123 ]]
then
echo "$foo is 123"
else
echo "$foo is not 123 or greater number"
fi
Single condition check
if [[ $foo > 123 ]]; then echo "$foo is more than 123";
fi
Case statement
Allows multi-pattern conditional statementGeneral syntax
case expression_or_variable in
pattern1)
statements1
;;
pattern2)
statements2
;;
patternX)
statementsX
;;
*)
fallbackStatements
;;
esac
Single line general syntax:
case expression_or_variable in pattern1) statements1 ;; pattern2) statements2 ;; patternX) statementsX ;; *)
fallbackStatements ;; esac
Example
case $foo in
123)
echo "$foo matches pattern 123"
;;
100 | "a b")
echo "$foo matches pattern 100 or \"a b\""
;;
1?3)
echo "$foo matches pattern 1?3"
;;
1*3)
echo "$foo matches pattern 1*3"
;;
*)
echo "$foo does not match other patterns"
;;
esac
Comparison
-
Checking is value of $foo is equal to
123if [[ $foo == 123 ]]; then echo equal; fiChecking is value of $foo is equal to123- integers onlyif [ $foo -eq 123 ]; then echo equal; fiChecking is value of $foo is equal toitem1 item2 item3if [[ $foo == 'item1 item2 item3' ]]; then echo equal; fi -
Checking is value of $foo is less than
123if [[ $foo < 123 ]]; then echo equal; fiChecking is value of $foo is less than123- only integersif [ $foo -lt 123 ]; then echo equal; fi -
Checking is value of $foo is greater than
123if [[ $foo > 123 ]]; then echo equal; fiChecking is value of $foo is greater than123- only integersif [ $foo -gt 123 ]; then echo equal; fi
shellcheck
Tool useful for script syntax and logic verification. Usage:
shellcheck path_to_script_file
This isn't antivirus or execution safety checker. It will not complain if script aim to erase all data with no way to recover it. It just provides hints on what could be done to improve likelihood of successful execution of script based on common errors and bad practices.