#!/bin/bash
The shebang (#!) must be on the very first line of the file, with no spaces before the # or between the ! and the path to the interpreter.chmod 755 script.sh
The shorthand 755 is often used for scripts as it allows you the owner to write or modify the script and for everyone to execute the script.
$1
,$2
, ...
The first, second, etc command line arguments to the script.variable=value
To set a value for a variable. No spaces on either side of=
.$variable
To read the value of a variable.- Quotes
"
'
Double quotes will do variable substitution, single quotes will not. variable=$( command )
Command substitution: save the output of a command into a variable.export var1
Make the variable var1 available to child processes.- Formatting: the presence or absence of spaces is important.
env
command: to see a listing of the environment variables.
Special Variables:
$0
- The name of the Bash script.$1
-$9
- The first 9 arguments to the Bash script.$#
- How many arguments were passed to the Bash script.$@
- All the arguments supplied to the Bash script.$?
- The exit status of the most recently run process (command or function).$$
- The process ID of the current script.$USER
- The username of the user running the script.$HOSTNAME
- The hostname of the machine the script is running on.$SECONDS
- The number of seconds since the script was started.$RANDOM
- Returns a different random number each time is it referred to.$LINENO
- Returns the current line number in the Bash script.
-
Command line arguments (accessed with
$1
,$2
, ...) -
Ask the user for input:
read
command is used to read input from the user interactively during script execution.
Two commonly used options are-p
to specify a prompt and-s
to make the input silent.# Ask the user for login details: read -p 'Username: ' uservar read -sp 'Password: ' passvar
-
Redirected from STDIN:
Bash accommodates piping and redirection by way of special files. Each process gets its own set of files (one for STDIN, STDOUT and STDERR respectively) and they are linked when piping or redirection is invoked.
/dev/stdin
is a file you can read to get the STDIN for the Bash script.
Then accept data via piping:command | ./script.sh
or redirection:./script.sh < datafile
.#!/bin/bash cat /dev/stdin
Double parentheses $(( expression ))
is the preferred method to do arithmetic in Bash scripts.
let expression
make a variable equal to an expressionexpr expression
print out the result of the expression$(( expression ))
return the result of the expression${#var}
return the length of the variable var (how many characters)
if [ <test> ]
then
<commands>
elif [ <test> ]
then
<commands>
else
<commands>
fi
Operator | Description |
---|---|
! EXPRESSION | The EXPRESSION is false |
-n STRING | The length of STRING is greater than zero |
-z STRING | The length of STRING is zero (i.e. it is empty) |
STRING1 = STRING2 | STRING1 is equal to STRING2 |
STRING1 != STRING2 | STRING1 is not equal to STRING2 |
INTEGER1 -eq INTEGER2 | INTEGER1 is numerically equal to INTEGER2 |
INTEGER1 -gt INTEGER2 | INTEGER1 is numerically greater than INTEGER2 |
INTEGER1 -lt INTEGER2 | INTEGER1 is numerically less than INTEGER2 |
-d FILE | FILE exists and is a directory |
-e FILE | FILE exists |
-r FILE | FILE exists and the read permission is granted |
-s FILE | FILE exists and it's size is greater than zero (i.e. it is not empty) |
-w FILE | FILE exists and the write permission is granted |
-x FILE | FILE exists and the execute permission is granted |
-
A string based vs numerical comparison:
$ test 001 = 1 $ echo $? 1
$ test 001 -eq 1 $ echo $? 0
-
Single vs. double square brackets:
- Single
[ … ]
is POSIX shell compliant, equivalent totest
. - Double
[[ … ]]
is a Bash extension that supports extra operations, for example:&&
and||
instead of-a
and-o
, lexicographical comparison with<
, regex matching with=~
.
- Single
-
Case Statements:
case <variable> in <pattern>) <commands> ;; <pattern>) <commands> ;; *) <catch all commands> ;; esac
-
While Loops:
while [ <test> ] do <commands> done
-
Until Loops:
until [ <test> ] do <commands> done
-
For Loops:
for var in <list> do <commands> done
- The list is a series of strings, separated by spaces:
'a b c'
. - The range is a series of numbers, with no spaces between the curly brackets:
{1..5}
or{$startingValue..$endingValue..$valueToStepBy}
.
- The list is a series of strings, separated by spaces:
-
break
: exit the currently running loop. -
continue
: stop this iteration of the loop and begin the next iteration. -
select
: display a simple menu system for selecting items from a list.select var in <list> do <commands> done
Example:
options='start stop restart quit' PS3='Select: ' select option in $options do if [ $option == 'quit' ] then break fi echo $option done
-
Creating a function:
The function definition must appear in the script before any calls to the function.
function_name () { <commands> }
or
function function_name { <commands> }
-
Passing Arguments:
Unlike other programming languages, in Bash the parenthesis () in a function declaration are there only for decoration and are never used to put arguments inside them. Instead the arguments are accessible within the function as $1, $2, etc:
#!/bin/bash # Passing arguments to a function: test_function () { echo $1 $2 } test_function arg1 arg2
-
Return Values:
-
Unlike other programming languages, Bash functions do not return a value, but a return status.
Usereturn <value>
to exit the function with a return status of value:#!/bin/bash # Setting a return status for a function: test_function () { return 5 } test_function echo The previous function has a return status of $?
-
Use command substitution to take what would normally be printed to the screen by a function and assign it to the variable:
#!/bin/bash # Setting a return value to a function: lines_in_file () { wc -l < $1 } num_lines=$( lines_in_file $1 ) echo The file $1 has $num_lines lines in it.
-
-
Variable Scope:
-
By default a variable is global.
-
Best practice is to create a local variable within a function:
local var_name=<var_value>
-
-
Overriding Commands:
command <command>
is used to run the command with that name as opposed to the function with the same name in order not to end up in an endless loop when called from within a function:#!/bin/bash # Create a wrapper around the command ls: ls () { command ls -la } ls
-
Good practice: always keep parameter expansions quoted:
"$parameter"
-
Good practice: use parameter expansions to modify strings instead of external applications that require an extra process to be started (sed, awk, cut, perl or others).
-
To get the file name:
"${file##*/}"
-
To get the file directory:
"${file%/*}"
-
To get the file extension:
"${file##*.}"
-
To remove the file extension:
"${file%.*}"
-
To rename all files with a .JPG or a .jpeg extension to have a .jpg extension:
for file in *.JPG *.jpeg do mv -- "$file" "${file%.*}.jpg" done
-
To add a common prefix to all elements of an array:
array=("${array[@]/#/$prefix}") printf '%s\n' "${array[@]}"
-
To add a common suffix to all elements of an array:
array=("${array[@]/%/$suffix}")
-
Difference between
${parameter#pattern}
and${parameter##pattern}
:
The doubling of the#
(match against the beginning character) or the%
(match against the end character) means patterns will become greedy:version=1.5.9 echo "major.minor.revision: $version" echo "major: ${version%%.*}" echo "minor.revision: ${version#*.}" echo "revision: ${version##*.}"
-
Parameter expansion summary:
Syntax | Description |
---|---|
${parameter:-word} | Use Default Value. If 'parameter' is unset or null, 'word' (which may be an expansion) is substituted. Otherwise, the value of 'parameter' is substituted. |
${parameter:=word} | Assign Default Value. If 'parameter' is unset or null, 'word' (which may be an expansion) is assigned to 'parameter'. The value of 'parameter' is then substituted. |
${parameter:+word} | Use Alternate Value. If 'parameter' is null or unset, nothing is substituted, otherwise 'word' (which may be an expansion) is substituted. |
${parameter:offset:length} | Substring Expansion. Expands to up to 'length' characters of 'parameter' starting at the character specified by 'offset' (0-indexed). If ':length' is omitted, go all the way to the end. If 'offset' is negative (use parentheses!), count backward from the end of 'parameter' instead of forward from the beginning. If 'parameter' is @ or an indexed array name subscripted by @ or *, the result is 'length' positional parameters or members of the array, respectively, starting from 'offset'. |
${#parameter} | The length in characters of the value of 'parameter' is substituted. If 'parameter' is an array name subscripted by @ or *, return the number of elements. |
${parameter#pattern} | The 'pattern' is matched against the beginning of 'parameter'. The result is the expanded value of 'parameter' with the shortest match deleted. If 'parameter' is an array name subscripted by @ or *, this will be done on each element. Same for all following items. |
${parameter##pattern} | As above, but the longest match is deleted. |
${parameter%pattern} | The 'pattern' is matched against the end of 'parameter'. The result is the expanded value of 'parameter' with the shortest match deleted. |
${parameter%%pattern} | As above, but the longest match is deleted. |
${parameter/pat/string} | Results in the expanded value of 'parameter' with the first (unanchored) match of 'pat' replaced by 'string'. Assume null string when the '/string' part is absent. |
${parameter//pat/string} | As above, but every match of 'pat' is replaced. |
${parameter/#pat/string} | As above, but matched against the beginning. Useful for adding a common prefix with a null pattern: "${array[@]/#/prefix}". |
${parameter/%pat/string} | As above, but matched against the end. Useful for adding a common suffix with a null pattern. |