Original Website
This is a quick reference to getting started with Bash scripting.
- Learn bash in y minutes (learnxinyminutes.com)
- Bash Guide (mywiki.wooledge.org)
Example
1 2 3 4 5 |
#!/usr/bin/env bash NAME="John" echo "Hello $NAME!" |
Variables
1 2 3 4 5 |
NAME="John" echo $NAME echo "$NAME" echo "${NAME}!" |
String quotes
1 2 3 4 |
NAME="John" echo "Hi $NAME" #=> Hi John echo 'Hi $NAME' #=> Hi $NAME |
Shell execution
1 2 3 4 |
echo "I'm in $(pwd)" echo "I'm in `pwd`" # Same |
Conditional execution
1 2 3 |
git commit && git push git commit || echo "Commit failed" |
Functions
1 2 3 4 5 6 |
get_name() { echo "John" } echo "You are $(get_name)" |
See: Functions
Conditionals
1 2 3 4 5 6 |
if [[ -z "$string" ]]; then echo "String is empty" elif [[ -n "$string" ]]; then echo "String is not empty" fi |
See: Conditionals
Strict mode
1 2 3 |
set -euo pipefail IFS=$'\n\t' |
See: Unofficial bash strict mode
Brace expansion
1 2 |
echo {A,B}.js |
Expression | Description |
---|---|
{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
Parameter expansions
Basics
1 2 3 4 5 6 7 8 9 10 |
name="John" echo ${name} echo ${name/J/j} #=> "john" (substitution) echo ${name:0:2} #=> "Jo" (slicing) echo ${name::2} #=> "Jo" (slicing) echo ${name::-1} #=> "Joh" (slicing) echo ${name:(-1)} #=> "n" (slicing from right) echo ${name:(-2):1} #=> "h" (slicing from right) echo ${food:-Cake} #=> $food or "Cake" |
1 2 3 |
length=2 echo ${name:0:length} #=> "Jo" |
See: Parameter expansion
1 2 3 4 5 6 7 8 9 10 11 12 13 |
STR="/path/to/foo.cpp" echo ${STR%.cpp} # /path/to/foo echo ${STR%.cpp}.o # /path/to/foo.o echo ${STR%/*} # /path/to 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 |
1 2 3 4 |
STR="Hello world" echo ${STR:6:5} # "world" echo ${STR: -5:5} # "world" |
1 2 3 4 |
SRC="/path/to/foo.cpp" BASE=${SRC##*/} #=> "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 |
Comments
1 2 |
# Single line comment |
1 2 3 4 5 6 |
: ' This is a multi line comment ' |
Substrings
Expression | Description |
---|---|
${FOO:0:3} |
Substring (position, length) |
${FOO:(-3):3} |
Substring from the right |
Length
Expression | Description |
---|---|
${#FOO} |
Length of $FOO |
Manipulation
1 2 3 4 5 6 7 8 |
STR="HELLO WORLD!" echo ${STR,} #=> "hELLO WORLD!" (lowercase 1st letter) echo ${STR,,} #=> "hello world!" (all lowercase) STR="hello world!" echo ${STR^} #=> "Hello world!" (uppercase 1st letter) echo ${STR^^} #=> "HELLO WORLD!" (all uppercase) |
Default values
Expression | Description |
---|---|
${FOO:-val} |
$FOO , or val if unset (or null) |
${FOO:=val} |
Set $FOO to val if unset (or null) |
${FOO:+val} |
val if $FOO is set (and not null) |
${FOO:?message} |
Show error message and exit if $FOO is unset (or null) |
Omitting the :
removes the (non)nullity checks, e.g. ${FOO-val}
expands to val
if unset otherwise $FOO
.
Loops
Basic for loop
1 2 3 4 |
for i in /etc/rc.*; do echo $i done |
C-like for loop
1 2 3 4 |
for ((i = 0 ; i < 100 ; i++)); do echo $i done |
Ranges
1 2 3 4 |
for i in {1..5}; do echo "Welcome $i" done |
With step size
1 2 3 4 |
for i in {5..50..5}; do echo "Welcome $i" done |
Reading lines
1 2 3 4 |
cat file.txt | while read line; do echo $line done |
Forever
1 2 3 4 |
while true; do ··· done |
Functions
Defining functions
1 2 3 4 |
myfunc() { echo "hello $1" } |
1 2 3 4 5 |
# Same as above (alternate syntax) function myfunc() { echo "hello $1" } |
1 2 |
myfunc "John" |
Returning values
1 2 3 4 5 |
myfunc() { local myresult='some value' echo $myresult } |
1 2 |
result="$(myfunc)" |
Raising errors
1 2 3 4 |
myfunc() { return 1 } |
1 2 3 4 5 6 |
if myfunc; then echo "success" else echo "failure" fi |
Arguments
Expression | Description |
---|---|
$# |
Number of arguments |
$* |
All positional arguments (as a single word) |
$@ |
All positional arguments (as separate strings) |
$1 |
First argument |
$_ |
Last argument of the previous command |
Note: $@
and $*
must be quoted in order to perform as described.
Otherwise, they do exactly the same thing (arguments as separate strings).
See Special parameters.
Conditionals
Conditions
Note that [[
is actually a command/program that returns either 0
(true) or 1
(false). Any program that obeys the same logic (like all base utils, such as grep(1)
or ping(1)
) can be used as condition, see examples.
Condition | Description |
---|---|
[[ -z STRING ]] |
Empty string |
[[ -n STRING ]] |
Not empty string |
[[ STRING == STRING ]] |
Equal |
[[ STRING != STRING ]] |
Not Equal |
[[ 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 |
More 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
1 2 3 4 5 6 7 8 9 |
# String if [[ -z "$string" ]]; then echo "String is empty" elif [[ -n "$string" ]]; then echo "String is not empty" else echo "This never happens" fi |
1 2 3 4 5 |
# Combinations if [[ X && Y ]]; then ... fi |
1 2 3 |
# Equal if [[ "$A" == "$B" ]] |
1 2 3 |
# Regex if [[ "A" =~ . ]] |
1 2 3 4 |
if (( $a < $b )); then echo "$a is smaller than $b" fi |
1 2 3 4 |
if [[ -e "file.txt" ]]; then echo "file exists" fi |
Arrays
Defining arrays
1 2 |
Fruits=('Apple' 'Banana' 'Orange') |
1 2 3 4 |
Fruits[0]="Apple" Fruits[1]="Banana" Fruits[2]="Orange" |
Working with arrays
1 2 3 4 5 6 7 8 9 |
echo ${Fruits[0]} # Element #0 echo ${Fruits[-1]} # Last element 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) echo ${!Fruits[@]} # Keys of all elements, space-separated |
Operations
1 2 3 4 5 6 7 8 |
Fruits=("${Fruits[@]}" "Watermelon") # Push Fruits+=('Watermelon') # Also 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
1 2 3 4 |
for i in "${arrayName[@]}"; do echo $i done |
Dictionaries
Defining
1 2 |
declare -A sounds |
1 2 3 4 5 |
sounds[dog]="bark" sounds[cow]="moo" sounds[bird]="tweet" sounds[wolf]="howl" |
Declares sound
as a Dictionary object (aka associative array).
Working with dictionaries
1 2 3 4 5 6 |
echo ${sounds[dog]} # Dog's sound echo ${sounds[@]} # All values echo ${!sounds[@]} # All keys echo ${#sounds[@]} # Number of elements unset sounds[dog] # Delete dog |
Iteration
Iterate over values
1 2 3 4 |
for val in "${sounds[@]}"; do echo $val done |
Iterate over keys
1 2 3 4 |
for key in "${!sounds[@]}"; do echo $key done |
Options
Options
1 2 3 4 5 |
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
1 2 3 4 5 6 |
shopt -s nullglob # Non-matching globs are removed ('*.foo' => '') shopt -s failglob # Non-matching globs throw errors shopt -s nocaseglob # Case insensitive globs shopt -s dotglob # Wildcards match dotfiles ("*.sh" => ".foo.sh") shopt -s 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.
History
Commands
Command | Description |
---|---|
history |
Show history |
shopt -s histverify |
Don’t execute expanded result immediately |
Expansions
Expression | Description |
---|---|
!$ |
Expand last parameter of most recent command |
!* |
Expand all parameters of most recent command |
!-n |
Expand n th most recent command |
!n |
Expand n th command in history |
!<command> |
Expand most recent invocation of command <command> |
Operations
Code | Description |
---|---|
!! |
Execute last command again |
!!:s/<FROM>/<TO>/ |
Replace first occurrence of <FROM> to <TO> in most recent command |
!!:gs/<FROM>/<TO>/ |
Replace all occurrences of <FROM> to <TO> in most recent command |
!$:t |
Expand only basename from last parameter of most recent command |
!$:h |
Expand only directory from last parameter of most recent command |
!!
and !$
can be replaced with any valid expansion.
Slices
Code | Description |
---|---|
!!:n |
Expand only n th token from most recent command (command is 0 ; first argument is 1 ) |
!^ |
Expand first argument from most recent command |
!$ |
Expand last token from most recent command |
!!:n-m |
Expand range of tokens from most recent command |
!!:n-$ |
Expand n th token to last from most recent command |
!!
can be replaced with any valid expansion i.e. !cat
, !-2
, !42
, etc.
Miscellaneous
Numeric calculations
1 2 |
$((a + 200)) # Add 200 to $a |
1 2 |
$(($RANDOM%200)) # Random number 0..199 |
Subshells
1 2 3 |
(cd somedir; echo "I'm now in $PWD") pwd # still in first directory |
Redirection
1 2 3 4 5 6 7 |
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) python hello.py &>/dev/null # stdout and stderr to (null) |
1 2 |
python hello.py < foo.txt # feed foo.txt to stdin for python |
Inspecting commands
1 2 3 |
command -V cd #=> "cd is a function/alias/whatever" |
Trap errors
1 2 |
trap 'echo Error at about $LINENO' ERR |
or
1 2 3 4 5 6 7 |
traperr() { echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}" } set -o errtrace trap traperr ERR |
Case/switch
1 2 3 4 5 6 7 8 9 10 |
case "$1" in start | up) vagrant up ;; *) echo "Usage: $0 {start|stop|ssh}" ;; esac |
Source relative
1 2 |
source "${0%/*}/../share/foo.sh" |
printf
1 2 3 4 5 6 7 8 9 |
printf "Hello %s, I'm %s" Sven Olga #=> "Hello Sven, I'm Olga printf "1 + 1 = %d" 2 #=> "1 + 1 = 2" printf "This is how you print a float: %f" 2 #=> "This is how you print a float: 2.000000" |
Directory of script
1 2 |
DIR="${0%/*}" |
Getting options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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
1 2 3 4 |
cat <<END hello world END |
Reading input
1 2 3 4 |
echo -n "Proceed? [y/n]: " read ans echo $ans |
1 2 |
read -n 1 ans # Just one character |
Special variables
Expression | Description |
---|---|
$? |
Exit status of last task |
$! |
PID of last background task |
$$ |
PID of shell |
$0 |
Filename of the shell script |
$_ |
Last argrument of the previous command |
See Special parameters.
Go to previous directory
1 2 3 4 5 6 |
pwd # /home/user/foo cd bar/ pwd # /home/user/foo/bar cd - pwd # /home/user/foo |
Check for command’s result
1 2 3 4 |
if ping -c 1 google.com; then echo "It appears you have a working internet connection" fi |
Grep check
1 2 3 4 |
if grep -q 'foo' ~/.bash_history; then echo "You appear to have typed 'foo' in the past" fi |
Also see
- Bash-hackers wiki (bash-hackers.org)
- Shell vars (bash-hackers.org)
- Learn bash in y minutes (learnxinyminutes.com)
- Bash Guide (mywiki.wooledge.org)
- ShellCheck (shellcheck.net)