Notes on Introduction to Advanced Bash Usage
While I am going through the following, the youtube talk and it’s associated presentation, my hand-ons were recorded here. It is recommended to go through the basics first. You can also refer to the Bash Ref Manual for more information.
- Parameters
- Expressions
- Pattern Matching
- Command Groups
- Redirection
- Parameter Expansion
- Arrays
- Arithmatics
- Brace Expansion
- Session Portability
Parameters
There are Positional parameters such as $1
, $2
and so on. if you use above 9, you must use braces such as ${11}
. You can find about special parameters from the documentation.
set -- first second
echo $*
echo $2 and $1
first second
second and first
If the command returns 0 when successful, otherwise number 1 -255. Use the $?
to capture the return value. Many programs signal different types of failure with different return values.
whoami
echo $?
ojitha
0
Use special variables such as $*
, $@
and so on has been explained in the following code snippet:
bash <<'EOF' -s -- Ojitha "How are you"
echo "$2 $1 ?"
echo arguement with *: $*
echo arguement with @: $@
echo number of arguments $#
j=0
for i in $@
do
echo "$j element: $i"
(( j = j+1 ))
done
EOF
How are you Ojitha ?
arguement with *: Ojitha How are you
arguement with @: Ojitha How are you
number of arguments 2
0 element: Ojitha
1 element: How
2 element: are
3 element: you
function login(){
if [[ ${@} =~ "-user" && ${@} =~ "-pass" ]]; then
echo "login as $2"
else
echo "Bad Syntax. Usage: -user [username] -pass [password] required"
fi
}
login -user "Ojitha" -pass "two"
login -pass "two" -user "one"
login "one" "two"
login as Ojitha
login as two
Bad Syntax. Usage: -user [username] -pass [password] required
There are compound commands
- iterations: such as
while
,until
,for
, andselect
. - conditions:
if
andcase
- command groups:
(list)
and {list;}`
i=0
while [ $i -lt 3 ]
do
echo i: $i
((i=i+1))
done
i: 0
i: 1
i: 2
Another example
The until
is the opposite of the while
.
seq 1 3 > a
while read e; do
printf '%d\n' $(( e ** 2 ))
done < a
1
4
9
i=0
until [ $i -gt 3 ]
do
echo i: $i
((i=i+1))
done
i: 0
i: 1
i: 2
i: 3
Following is C lang style For loop
for (( i=0 ; i < 3 ; i++ ))
do
echo $i
done
0
1
2
here the iterator
for i in $(seq 1 3) ; do echo $i; done
1
2
3
or
seq 1 3 | while read line; do printf '%d\n' $((line)); done
1
2
3
as well as
for i in a b "c and d"
do
echo "=> $i"
done
=> a
=> b
=> c and d
or
for i in {0..8..2}
do
echo $i
done
0
2
4
6
8
The select
statement example is hard to show in the notebook because it is interactive.
select choice in one two "no choice"
do
echo $choice
done
will give you to select the choice
1) one
2) two
3) no choice
#?
You can select 1, 2 or 3 from the stdin.
Expressions
The []
matches any one of the enclosed characters. The new way is [[...]]
, which returns a status of 0 or 1 depending on the evaluation of the conditional expression.
s1="ojitha"
s2=""
[[ -n $s1 ]]
echo "s1: $?"
# test string is non-empty
[[ -n $s2 ]]
echo "s2: $?"
s1: 0
s2: 1
To test string is empty:
s2=""
[[ -z $s2 ]]
echo "s2: $?"
s2: 0
test the strings are the same
s1="ojitha"
s2="hewa"
# matched
[[ 'Hello' == 'Hello' ]]
echo $?
# unmatched
[[ 'Hello' == 'ojitha' ]]
echo $?
0
1
To match the regular expression.
set -- "the pen is my brother's" # set $1 to sample string
RE='^the' # regex
[[ $1 =~ $RE ]]
echo $?
0
or
a=ojitha
[[ $a == +(oj|aj)itha ]]
echo $?
0
The return value 0
is a successful match.
Expression | Description |
---|---|
[[ -e file ]] |
file exists |
[[ -f file ]] |
file is a regular file |
[[ -d file ]] |
file is a directory |
[[ -t fd ]] |
fd idopen and refer to terminal |
In the conditional statement:
if [[ 'Hello' == 'oj' ]] then
echo 'true'
else
echo "false"
fi
false
Use with regex
a=ojitha
if [[ $a == +(oj|aj)itha ]]; then echo yes; else echo no; fi
yes
Pattern Matching
The case
is a great way to match patterns. The pipe character between two patterns entails a match on condition OR
.
case "ojitha Hello" in
o)
echo 'o'
;;
o*)
echo 'o*'
;;
*)
echo 'nope'
;;
esac
o*
Command Groups
Evaluate the list of commands in a subshell using (...)
.
NOTE: To evaluate a list of commands in the current shell, use
{ list; }
. Here the spaces next to the curly braces and trailing semicolon are compulsory.
a="Ojitha"
( # subshell
# inside
a="Disinee"
echo "In the subshell $a"
)
# outside
echo "Outside the subshell $a"
In the subshell Disinee
Outside the subshell Ojitha
If you use the group command
a="Ojitha"
{ # group command
# inside
a="Disinee"
echo "In the subshell $a"
}
# outside
echo "Outside the subshell $a"
In the subshell Disinee
Outside the subshell Disinee
echo b; echo a | sort
echo ------ in the subshell ---
(echo b; echo a) | sort # subshell
b
a
------ in the subshell ---
a
b
Another use of parenthesis:
echo I am $(whoami)
echo I am `(whoami)` #same as above
I am ojitha
I am ojitha
Redirection
If not specified, fd 1
(STDOUT
) is assumed when redirecting output. Alternative file descriptors may be specified by prepending the fd number
. To direct error to file: 2>file
. To redirect to a file descriptor, append &
and the fd number
: 2>&1
to redirect STDERR
to the current target for STDOUT
.
In the above, $(whoami)
is a command substitution. The Process substitution feeds the output of a process (or processes) into the stdin
of another process. For example, I want to compare two directories:
First, create directories and files inside the directories.
mkdir first && touch first/f1.txt first/f2.txt first/common.txt
mkdir second && touch second/s1.txt second/s2.txt second/common.txt
to compare
diff <(ls first) <(ls second)
output
2,3c2,3
< f1.txt
< f2.txt
---
> s1.txt
> s2.txt
NOTE: The file common.txt is not visible because that exists in both of the directories.
Parameter Expansion
Find the documentation.
Following code, if the param
is empty, it returns the default (in this case $(whoami)
) value but is not set to the variable.
who=""
echo who without default: $who
echo who with default: ${who:-$(whoami)}
echo who without default: $who
who without default:
who with default: ojitha
who without default:
if you want to set the default to the who
variable (use :=
).
Positional parameters and special parameters may not be assigned in this way.
who=""
echo who without default: $who
echo who with default: ${who:=$(whoami)}
echo who without default: $who
who without default:
who with default: ojitha
who without default: ojitha
Alternate
who=ojitha
echo ${who+aaa}
echo $who
aaa
ojitha
as well as
who=ojitha
echo ${who:+aaa}
echo $who
aaa
ojitha
If a parameter is null or unset, the expansion of the word (or a message to that effect if the word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of the parameter is substituted.
who=
echo ${who:?variable is null}
Substring
who=ojitha
echo ${who:2}
echo ${who:2,3}
echo ${who#oj} # left edge
echo ${who%ha} # right edge
greedy=ojitha:hewa:kuma
echo ${greedy##*:} # greedy
itha
tha
itha
ojit
kuma
From right to left
who=ojitha
echo ${who:(-1)}
a
Expand to length
who=ojitha
echo length ${#who}
# indirect expansion
${!who}
length 6
Indirect expansion
surname=lastname; lastname=HEWA
echo ${!surname}
HEWA
List name-matching prefix
v1=""
v2=""
v3=""
v4=""
echo "${!v@}"
echo ${!v*}
v1 v2 v3 v4
v1 v2 v3 v4
List the keys in an array ${!arr[*]}
or ${!arr[@]}
Pattern Substituion
who=abba
echo ${who/b/x}
# greedy
echo ${who//b/x}
axba
axxa
substitute at left edge
who=aabb
echo ${who/#aa/12}
12bb
substitute at the right edge
who=aabb
echo ${who/%bb/12}
aa12
Arrays
There are arrays and new associative arrays. First see the arrays:
# declare the array
a=( 1 4 3 ojitha "Hello World" )
declare -p a
# add to array
a+=("Hewa")
declare -p a
echo "second element: ${a[1]}"
# print array indexes
echo Array indexes ${!a[@]}
declare -a a=([0]="1" [1]="4" [2]="3" [3]="ojitha" [4]="Hello World")
declare -a a=([0]="1" [1]="4" [2]="3" [3]="ojitha" [4]="Hello World" [5]="Hewa")
second element: 4
Array indexes 0 1 2 3 4 5
substitute _
for all the spaces
# declare the array
a=( "This is an example of substituting underscore for spaces" "Hello World" )
a=( "${a[@]// /_}" )
declare -p a
declare -a a=([0]="This_is_an_example_of_substituting_underscore_for_spaces" [1]="Hello_World")
Sub arraying
# declare the array
a=( 1 2 3 ojitha "Hello World" )
a=( ${a[@]:2:3} )
declare -p a
declare -a a=([0]="3" [1]="ojitha" [2]="Hello" [3]="World")
Passing an array argument
#variable
a=(1 2 3 4)
function display(){
arr=($*)
for i in ${arr[@]}
do
echo $i
done
}
display ${a[@]}
1
2
3
4
Associative arrays
# Declare an associative array
declare -A arr
arr[fruit]=Mango
arr[bird]=Cockatail
arr[flower]=Rose
arr[animal]=Tiger
for i in ${!arr[*]}
do
echo $i
done
flower
fruit
animal
bird
Arithmatics
((1==1)); echo "1==1 $?"
((1!=1)); echo "1!=1 $?"
((1<2)); echo "1<2 $?"
((1>2)); echo "1>2 $?"
# adding
echo 1+1 = $((1+1))
# counter
count=1
# after evaluate
echo "count=1, count++ after evaluation is $((count++))"
# before evaluate
count=1
echo "count=1, ++count before evaluation is $((++count))"
1==1 0
1!=1 1
1<2 0
1>2 1
1+1 = 2
count=1, count++ after evaluation is 1
count=1, ++count before evaulation is 2
# counter
count=5
# after evaluate
echo "count=5, count++ after evaluation is $((count--))"
# before evaluate
count=5
echo "count=5, ++count before evaluation is $((--count))"
count=5, count++ after evaluation is 5
count=5, ++count before evaluation is 4
Brace Expansion
Generate arbitrary words
echo walk{,e{d,s},ing,ful{l,ly}}
walk walked walkes walking walkfull walkfully
echo {1..5}{0,5}%
10% 15% 20% 25% 30% 35% 40% 45% 50% 55%
Session Portability
Import elements from the current session directly into a new local or remote session.
p
for parametersf
for functions
#variable
who=ojitha
function hello(){
echo Hello $1
}
# pass a variable and the function to a new shell
bash -c "$(declare -p who; declare -f hello); hello $who "
Hello ojitha
You can pass the session to remote
ssh remote_host "$(declare -p parameter; declare -f func); your code "