Differences between shell functions and other scripts (e.g. python scripts)
Functions are executed in the current shell environment whereas scripts execute in their own process. Thus, functions can modify environment variables, e.g. change your current directory, whereas scripts can’t. Scripts will be passed by value environment variables that have been exported using export
Variables
a=Hello# no spaces!# a = Hello means run a with = and Hello as argumentsecho $a # Helloecho"$a"# Helloecho'$a'# $aechoa# a# add attributes to variablesdeclare-i d=123# d is an integerdeclare-r e=456# e is read-onlydeclare-l f="LOLCats"# f is lolcatsdeclare-u g="LOLCats"# g is LOLCATS# Built-in varibles# $HOME $PWD $MACHTYPE $HOSTNAME $RANDOM# $BASH_VERSION related: bash --version# $0 name of the script# $SECONDS time since this bash session had run / since the script started# env: shows all environment variablesd=$(pwd) # run pwd and put the result in d.# Arithmetic (only integers are supported)val=$((expression))# operators: **, %, +-*/%e=5e+=2# string concatenation((e+=2)) # arithmetic
Command substitution
d=$(pwd) # run pwd and put the result in d.
Process substitution
command-a<(CMD)# execute CMD, put output in a temp file,# replace <() with the temp file name.# useful when command expects input as a file instead of STDIN# Example:mkdirfoobar# This creates files foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/htouch{foo,bar}/{a..h}touchfoo/xbar/y# Show differences between files in foo and bardiff<(lsfoo)<(lsbar)# Outputs# < x# ---# > y# Note that in python os.system(cmd) uses sh# to use process substitution, do# os.system('/bin/bash -c "cmd"')
Comparisons
[[ expression ]] # Note the spaces# false/failure: returns 1# true/success: returns 0 [[ "cat"=="cat" ]]echo $? # $? stores the previous value# comparison for integers# 20 > 100 is true because its string comparison[[ 20-lt100 ]]# -lt less than, -gt, -le less than or equal to, -ge, -eq, -ne# &&, ||, ! logic operators#string is null value (empty string)[[ -z $a ]] # is null?# -n => is not null?
Strings
# concatenationa=Hellob=Worldc=$a$becho $c # HelloWorld# get string's lengthecho ${#a} # 5# substring# start index, lengthecho ${c:3:4} # olWo# last four characters. Note the space before dash.f=${c: -4} # orld# replacefruit="apple banana banana cherry"# replace the first occuranceecho ${fruit/banana/durian} # apple durian banana cherry# replace all occuranceecho ${fruit//banana/durian} # apple durian durian cherry# replace only if banana is the beginning of the stringecho ${fruit/#banana/durian}# replace only if banana is the end of the stringecho ${fruit/%banana/durian}# replace regex matchesecho ${fruit/b*/durian} # apple durian
Reading and Writing Text Files
echo"Some text">file.txt# If already exsits, override, else, create.> file.txt # clear content of a fileecho"Some text">>file.txt# append to end# readcat<file.txt# read line by linewhilereadf; doecho $fdone< file.txt# Example: set a list of instructions# create a file ftp.txt with commandsftp-n<ftp.txt
Control Structures
if
if [ expression ] # or if [[ $a =~ regex ]] (( integer comparison )) or no brackets at allifexpression; then...ifexpressionthenecho"true"elif...else...fi
note that when expression succeeds (return value is 0), then clause runs. (not like JavaScript, hwere 0 is falsey)
for i in123# or 1 in {1..100}doecho $idone# C style for loopfor (( i=1; i<=10; i++ ))do...done# arrayarr=("apple""banana")for i in ${arr[@]}do...done# associative array (BASH 4.0+)declare-A arrarr["name"]="scott"arr["id"]="123"for i in"${!arr[@]}"# use quote to handle spaces in keysdoecho"$i: ${arr[$i]}"done# command substitutionfor i in $(ls)doecho"$i"done
Case (switch in C)
a="dog"case $a incat)echo"Feline";;dog|puppy)echo"Canine";; # dog or puppy*)echo"No match!";; # defaultesac# case spelled backwards
Array
a=() # empty arrayb=("apple""banana""cherry")echo ${b[2]} # cherryb[5]="kiwi"b+=("mango") # appendecho ${b[@]} # the whole arrayecho ${b[@]: -1} # last element# Associate arrays (BASH4.0+)declare-A myarraymyarray[color]=blue# if key/value has spaces, use quotes
Functions
functiongreet {echo"Hi"}# invokegreet# with argumentsfunctionsayHi {echo"Hi, $1"}sayHiDavidfunctionnumberThings { i=1for f in $@; do# $@ is the arguments arrayecho $i: $f (( i +=1 ))done}numberthings $(ls)
Arguments
Anything with space in it needs quotes.
$0: name of the script
$1: first argument
$#: the number of arguments
$@: the arguments array
$?: return code of previous command
$$: PID of current script
$_: last argument of previous command
#!/bin/bashecho"Starting program at $(date)"# Date will be substitutedecho"Running program $0 with $# arguments with pid $$"for file in $@; dogrepfoobar $file >/dev/null2>/dev/null# When pattern is not found, grep has exit status 1# We redirect STDOUT and STDERR to a null register since we do not care about themif [[ $? -ne0 ]]; thenecho"File $file does not have any foobar, adding one"echo"# foobar">>"$file"fidone
Interact with the User
Working with flags
# Case one: get u and p values# my.shwhilegetoptsu:p:option; docase $option inu) user=$OPTARG;;p) pass=$OPTARG;;esacdone# in shell, call with ./my.sh -u scott -p 123# Case two: boolean flagswhilegetopts:u:p:aboption; do# ab has no colon after them. They are boolean flags.# `:` before u => we care about unknown flagscase $option inu) user=$OPTARG;;p) pass=$OPTARG;;a)echo"A";;b)echo"B";;?)echo"I don't know $OPTARG";;esacdone
Get input during execution
echo"What is your name?"readname# wait for user to input, and store in nameread-spass# silent. Don't print the input password on screenread-p"What's your age?"age# -p promptselect option in "cat""dog""bird""quit"docase $option incat)echo"you selected $animal";;quit)break;;*)echo"I don't understand"esacdone
Ensuring a response
What if user just press enter?
if [ $# -lt3 ]; thencat<<-EOM some error messageEOMelse# the program herefiread-p"Name? "nwhile [[ -z"$n" ]]; doread-p"Need a name! "ndone# provide a default answerread-p"Name? [David] "nwhile [[ -z"$n" ]]; do n=Daviddone# input validation by regexread-p"What year? [nnnn] "awhile [[ ! $a =~ [0-9]{4} ]]; doread-p"A year, please! [nnnn] "adone
Date and Printf
datedate+"%d-%m-%Y"date+"%H:%M:%S"printf"Name:\t%s\nID:\t%04d\n""Name""12"# 4 digit with padding zerosprintf-vd"strings"# store the string in variable d#command substitution strips out the newlines
Advanced Topics
Debug Mode
# run script in normal mode$basha.sh# run in debug mode# prints out each command as it is run# or you can set -x in a.sh# set -e => exit script when there is an error$bash-xa.sh
Brace Expansion
~ $ echo {apple,banana}applebanana~ $ echo {apple,banana}{apple, banana} # does not work with spaces~ $ touch{apple,banana}# creates two files~ $ echo {1..10}12345678910~ $ echo {A..z}ABCDEFGHIJKLMNOPQRSTUVWXYZ [ ]^_` abcdefghijklmnopqrstuvwxyz# uppercase letters are before lowercases~ $ echo {a..Z}a `_^] [ Z# reverse order~ $ echo {apple,banana}_{1..3}apple_1apple_2apple_3banana_1banana_2banana_3# combinations# in bash 4+~ $ echo {01..1000}# add padding zeros~ $ echo {1..10..2}# specify intervals
Print Colored Text
ANSI escape codes: e.g. -e can enable escape sequences (start + string to print + end).
Start: '\033[number1;number2m'. Number1 and number 2 are foreground and background colors. m indicates the end of sequence. A style number (followed by ;) before number1 is optional.
End: usually it's '\033[0m', to clear all the formatting.
Color
Foreground
Background
Black
30
40
Red
31
41
Green
32
42
Yellow
33
43
Blue
34
44
Magenta
35
45
Cyan
36
46
White
37
47
Style table
Style
Value
No Style
0
Bold
1
Low Intensity
2
Underline
4
Blinking
5
Reverse
7
Invisible
8
echo-e'\033[34;42mColor Text\033[0m'# store formatting in variablesflashred="\033[5;31;40m"end="\033[0m"echo-e $flashred"Error"$end
Alternative: use tput.
Here Documents
cat<<endoftextthis is amultilinetextendoftext# with <<-, bash will strips off leading tabscat<<-endoftext2>log.txt this is a multiline textendoftext2ftp-n<<-label commandslabel