Bash

Program execution

#!/bin/sh or #!/usr/bin/sh # Single line comment : ' Multi-line comment
(:) is a null command
single quotes for literal characters
'
command arg1 arg2 arg3 ...# basic command command arg; command arg; command# multiple commands in one line command arg | command arg | command# pipeline
# I/O Redirection # - stdin = fd 0   stdout = fd 1   stderr = fd 2 python hello.py > output.txt# redirect stdout to file python hello.py >> output.txt# append stdout to file python hello.py 2> error.log# redirect stderr to file python hello.py 2>&1# stderr to stdout (dup 1 to 2) python hello.py 2>/dev/null# stderr to (null) python hello.py >output.txt 2>&1# stdout and stderr to (file) python hello.py &>/dev/null# stdout and stderr to (null) python hello.py >&-# close stdout python hello.py <&-# close stdin python hello.py 3>&- or 3<&-# close fd 3 echo "Warning: too many users" >&2# redirect stdout to stderr cat foo.txt | grep test# pipe stdout to stdin find . -print0 | xargs -0 rm# pipe to args
# Process substitution # - redirects stdout to a temp file at /dev/fd/<n> # - the temp filename is substituted in its place cat <(echo 'hello')# hello echo 'hello' > >(cat)# hello diff <(ls -r) <(ls)# diff between file lists Note: requires no spaces between <(
# Shell execution source config.sh# current shell execution . config.sh# same as source \. config.sh# same as source, ignoring aliases for . echo "I'm in $(pwd)"# command substitution {cd /tmp && ls}# command list execution in current shell (cd /tmp && ls)# command list execution as subprocess bash -c 'env | grep CLASSPATH'# new shell execution as subprocess ./config.sh# run script in new shell as subprocess pwd# run command as subprocess exec pwd# replace current shell with command exec > output.txt# apply redirections to current shell
# Conditional execution git commit && git push# shortcut AND git commit || echo "Commit failed"# shortcut OR

 

# BG / FG / multiprocess execution pwd &# run in background nohup pwd &# run in background will not exit on logout nohup pwd > foo.out 2> foo.err < /dev/null &# redirect stdin stdout stderr to prevent hanging nice -n +19 sleep 10# run with lowest priority (highest niceness +19) nice -n -20 sleep 10# run with highest priority (lowest niceness -20) nice -n -20 sleep 10# run with highest priority (lowest niceness -20) jobs# show jobs and job IDs jobs -l# show jobs (process groups) with PIDs fg# move previous job to foreground bg# move previous job to background fg %<n># move job ID <n> to foreground bg %<n># move job ID <n> to background disown %<n># prevent job ID %<n> from exit on logout wait PID# wait on PID and return exit status wait %<n># wait on job ID <n> and return exit status kill SIG PID# send signal SIG to PID kill -l# list signal names # 15 SIGTERM graceful shutdown (default) #  9 SIGKILL shutdown #  3 SIGQUIT graceful shutdown with core dump #  2 SIGINT interrupt and return control to user #  1 SIGHUP hang up aka log out # 19 SIGSTOP 18 SIGCONT stop continue aka pause resume
# Trap errors trap 'echo Error at about $LINENO' ERR traperr() {
  echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}"
}
set -o errtrace
trap traperr ERR
# Brace expansion ls {A,B}.sh# same as ls A.sh B.sh ls A/{B,C/{1..3}}.sh# A/B.sh A/C/1.sh A/C/2.sh A/C/3.sh
# Filename expansion (aka globbing) ls# .a.sh a.sh b.sh c.sh cc.sh ls *.sh# a.sh b.sh c.sh cc.sh ls a*# a.sh ls [c]*.sh# c.sh cc.sh ls c?.sh# cc.sh ls [^a].sh# a.sh Note: Strings containing wildcards will not match filenames that start with a dot (.) # Glob options 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') Note: Set GLOBIGNORE as a colon-separated list of patterns to be removed from glob matches.

Variables

text="*.txt"# cat $text# all .txt file contents cat "$text"# *.sh cat '$text\n'# $text\n cat $'$text\n'# $text<newline> unset text Note: generally double-quote variables unless they
contain wildcards to expand or command fragments
# Default values ${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) Note: (:) includes null tests
Arguments $## 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).
Special variables $?# Exit status of last task $!# PID of last background task $$# PID of shell $0# Filename of the shell script Note: $0 is not a positional argument. ${PIPESTATUS[n]}# Exit status command n in a pipeline (array) # Copy the array first before inspection because
# the first access will write new values into the array
RC=( "${PIPESTATUS[@]}" )
# Indirection name=joe pointer=name echo ${!pointer}# joe
# Prefix name expansion prefix_a=one prefix_b=two # all variables names starting with prefix_ echo ${!prefix_*}# prefix_a prefix_b

 

# Substitution ${foo%suffix}# Remove suffix ${foo#prefix}# Remove prefix ${foo%%suffix}# Remove long suffix ${foo/%suffix}# Remove long suffix ${foo##prefix}# Remove long prefix ${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
# String Manipulation foo='foobar' echo "${#foo}"# 6 Length of $foo echo "${foo:1:3}"# oob Substring (position, length) echo "${foo:1}"# oobar Substring to the end echo "${foo:(-3):3}"# bar Substring from the end echo "${foo::-1}"# fooba Substring until length-1 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)
# Numeric calculations $((a + 200))# POSIX integer arithmetic, returns string $(($RANDOM%200))# Random number 0..199 declare -i count# Declare as type integer count+=1# Increment

Control Flow

# if / elif / else statement if [[ -z "$string" ]]; then   echo 'empty' elif [[ -n "$string" ]]; then   echo 'not empty' else   echo fi
[ -z $string ]# POSIX test [[ -z $string ]]# bash test w/ extended features ((3 - 3))# bash arithmetic test w/ extended feature
  returns zero when non-zero
Note: [ and ] and [[ and ]] require whitespace around them
# Case / switch statement case "$1" in   (start | up)     vagrant up ;;   (*)     echo "Usage: $0 {start|stop|ssh}"     ;; esac Note: opening parentheses are optional and commonly omitted
# Loops # Basic for loop for i in /etc/rc.*; do   echo "$i" done # C-like for loop for ((i = 0 ; i < 100 ; i++)); do   echo "$i" done # Range for loop for i in {1..5}; do   echo "$i" done # Range for loop with step size for i in {5..50..5}; do   echo "$i" done # Infinite loop while true; do   ... done
# Functions hello() {   echo "hello $1" } function hello {   echo "hello $1" } hello "world"# hello world

Conditions

# Test conditions [[ -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 [[ -o OPTIONNAME ]]# OPTIONNAME enabled [[ ! EXPR ]]# Not [[ X && Y ]]# And [[ X || Y ]]# Or # File conditions [[ -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 Note: [[ is a command/program that returns
either 0 (true) or 1 (false). Any program that obeys
the same logic can be used as a condition.

Functions

# Returning values myfunc() {   local myresult='some value'   echo "$myresult" } result=$(myfunc)
# Raising errors myfunc() {   return 1 } if myfunc; then   echo "success" else   echo "failure" fi

Reading input

# Reading input echo -n "Proceed? [y/n]: " read -r ans echo "$ans" Note: read's -r option disables a
peculiar legacy behavior with backslashes.
read -n 1 ans # Read one character
# Read lines loop while read -r line; do   echo "$line" done < file.txt
# Parsing options 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
# Here-doc cat << END
hello world
END
# hello world
# Here-string cat <<< abc.txt# abc.txt

Arrays

# Array definition Fruits=('Apple' 'Banana' 'Orange' ) Fruits[0]="Apple" Fruits[1]="Banana" Fruits[2]="Orange"
# Array access 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
# Array operations 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
# Array iteration for i in "${arrayName[@]}"; do
  echo "$i"
done

Dictionaries / Associative Arrays

# Dictionary definition declare -A sounds# declares as associative array sounds[dog]="bark" sounds[cow]="moo" sounds[bird]="tweet" sounds[wolf]="howl"
# Dictionary access echo "${sounds[dog]}"# Dog's sound echo "${sounds[@]}"# All values echo "${!sounds[@]}"# All keys echo "${#sounds[@]}"# Number of elements unset sounds[dog]# Delete dog
# Iterate over values for val in "${sounds[@]}" ; do
  echo "$val"
done
# Iterate over keys for key in "${!sounds[@]}" ; do
  echo "$key"
done

History

# History history# Show history shopt -s histverify# Don’t execute expanded result immediately !!# Execute last command again !n# Expand nth command in history !-n# Expand nth most recent command !<command># Expand most recent invocation of command !!: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.
# History slices !!:n# Expand only nth token from most recent command (command is 0; first argument is 1) !^# Expand first argument from most recent command, short for !!:^ !$# Expand last token from most recent command, short for !!:$ !*# Expand all parameters of most recent command !!:n-m# Expand range of tokens from most recent command !!:n-$# Expand nth token to last from most recent command !! can be replaced with any valid expansion i.e. !cat, !-2, !42, etc.

Miscellaneous

# Transform strings -c# Operations apply to characters not in the given set -d# Delete characters -s# Replaces repeated characters with single occurrence -t# Truncates [:upper:]# All upper case letters [:lower:]# All lower case letters [:digit:]# All digits [:space:]# All whitespace [:alpha:]# All letters [:alnum:]# All letters and digits # Example echo "Hello World" | tr '[:lower:]' '[:upper:]' HELLO WORLD
# Strict mode set -euo pipefail IFS=$'\n\t'
# Options 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
printf 'IFS is: %q' "$IFS"# print IFS source "${0%/*}/../share/foo.sh"# source relative dir=${0%/*}# command location find . -iname text.txt# find file location locate text.txt# find file location realpath $1# absolute path basepath $1# file name basepath $1 .sh# file name without suffix

 

# Inspect commands which cd# find command location type cd# cd is a function/alias/shell builtin/... command -V cd# cd is a function/alias/shell builtin/... man cd# manual/man pages for cd
Go to previous directory pwd# /home/user/foo cd bar/ pwd# /home/user/foo/bar cd - pwd # /home/user/foo
Check for command’s result if ping -c 1 google.com; then   echo "It appears you have a working internet connection" fi
Grep check if grep -q 'foo' ~/.bash_history; then   echo "You appear to have typed 'foo' in the past" fi
# printf 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" printf '%s\n' '#!/bin/bash' 'echo hello' < file # format string is applied to each group of arguments printf '%i+%i=%i\n' 1 2 3 4 5 9