#! (sha-bang) (points to executable that runs the script)
#!/bin/sh
#!/bin/bash
#!/usr/bin/python3
Comments
# this is a comment
Executing remember to give executable permission (x)
$ ./test.sh executes; but doesn't export variables to environment
$ bash test.sh no need of +x permission; doesn't export variables to environment
$ sh test.sh same as above (but uses "sh" as shell)
$ source test.sh executes and exports variables to environment
$ . test.sh dot (.) is just a shorthand for the "source" command
Quotes and Backslash:
${}, $()) inside; wildcards not allowedMy\ Documents)Case-sensitive, no special characters except underscore (_)
Creation:
#!/bin/sh
echo $name # nothing printed, by default -> empty var
name=John # assignment
echo Hello $name! # usage
readonly age=55 # a constant
unset name # reset variable's value to empty
# empty vars
foo=
bar=""
Spaces between operands and operators are not at all valid:
name = John # error; command "name" not found
name=John # valid
Usage:
$VAR_NAME : A $ prefix is required only when using the variable; not during assignment
Using ${} to use variable value:
REALM=Ana
echo "You're in ${REALM}heim"
:=): a default value can be set if variable is empty at the time of usageecho ${NAME:="John"}
:?): stops execution if variable is empty${varName:?Error varName is not defined}
Scopes: Environment (all currently running child terminals can access them; session-scope) and Local (function only)
export myvar="foobar" # "exporting" variable myvar to environment
. test.sh # "sourcing" the script's variables to environment
Interactive
read username
# a prompt and variable input in the same line
read -p 'Enter username: ' name
# secret; don't show input in terminal output
read -s -p 'Enter password: ' password
# read array; space separated array input (by default)
read -a names
echo ${names[0]} ${names[1]} # usage
Command-line arguments
$0 script name
$1 ... $n command line args
$# number of cmd line args the scipt is called with
$* wrap all args in a single double-quotes
$@ wrap individual args in double-quotes separately
$$ current PID
$? exit status of prev command, 0 = success, 1 = failure
Use either backticks (``) or $()
echo "Today's date is `date`"
echo "Today's date is $(date)"
# Both of the above lines print: Date is Tue Oct 4 01:53:55 IST 2022
echo -e 'foo\nbar' allow escape characters inside, in any quotes
echo * wildcard; lists directory contents (like `ls`)
echo *.png list all .png files in pwd
printf "Marks: %d" $num just like in C lang
# sleep for 30 sec
sleep 30s
#sleep for half-a-minute
sleep 0.5m
# sleep for 1 hour
sleep 1h
Arithmetic: + - * / % **
$(( expression )) is used for arithmetic evaluation followed by sustitution (in-place of expression).
$(( expression )): Evaluates the expression. Return value is the value of expression after evaluation.
(( expression )): This too evaluates the expression in the same way as above. But, if the value of the expression is non-zero, the return value is 0; otherwise the return value is 1 (yes, this if flipped than in C! value 0 fed to if will execute its clause). So, this is often used with conditionals like if, else etc…
echo 3+4 # there are no types in BASH, only String (prints "3+4")
echo $(( 2 + 3 )) # spaces doesn't matter here
echo $(( $num1 + $num2 )) # usage with vars
expr 2 + 3 # spaces matters here (prints "2+3", if no spaces)
expr $num1 + $num2 # with vars
expr 4 \* 5 # need to escape * with expr or syntax error! (weird)
Unary Operators: ++ -- (post and pre)
i=0
while [ $i -lt 10 ]
do
echo $i
((i++)) # notice
done
Relational:
ans=$(( 5 < 29 ))
echo $ans
# prints "1" (non-zero means True)
For evaluating to a boolean, use [] and below forms of the relational operators:
-gt greater than
-lt less than
-e equal to (==)
-ne not equal to (!=)
-le less than or equal to
-ge greater than or equal to
Usage (all spaces matter): [ $n1 -gt $n2 ]
As a rule of thumb, always use < symbols inside (( condition )) and -lt forms inside [ condition ].
Sometimes we also use [[ condition ]] (newer) (like with an if clause). The advantage being that both -lt forms as well as the < symbols work inside it.
[[ ]] is more safer to use (no glob expansion), but the trade-off is backwards compatibility as it is much newer.
Reference: http://mywiki.wooledge.org/BashFAQ/031
Logical:
&& -a AND operator
|| -o OR operator
! NOT operator
Usages: [[ && ]]
[ -a ]
[] && []
# Examples
if [ $a -lt 100 -o $b -gt 100 ]
String operators:
[ $a = $b ] true if equal
[ -z $b ] true if length is zero ("")
[ $a ] true if not empty variable
< > != == compare strings with relational operators
With Strings, Use < symbols inside [[ condition ]]. The (( condition )) operator doesn’t work with Strings.
File tests:
file="my.txt" file names can be kept as Strings; context will be inferred upon usage with file test operators
[ -d $file ] true if directory
[ -f $file ] true if file
[ -r $file ] true if readable
[ -w $file ] true if writable
[ -x $file ] true if executable
[ -e $file ] true if exists
All test operators [ ], [[ ]] or (( )) convert a non-zero value inside them to a return value of 0, and the if clause is executed in that case. So, if an if clause have a non-zero value, it is executed, otherwise else.
if-then-elif-then-else-fi
if [ ... ]
then
#body
else
fi
# alt syntax #1
if (( ... ))
then
#body
else
fi
# alt syntax #2
if [[ ... ]]
then
#body
else
fi
if [ ... ]; then #if and then needs to be on different lines or use ";"
elif
then
else #if both the above "ifs" fail
fi
CASE Statements:
fruit="kiwi"
case $fruit in
"apple") echo "Apple pie is quite tasty."
;;
"banana") echo "I like banana nut bread."
;;
"kiwi") echo "New Zealand is famous for kiwi."
;;
*) echo "Default Case."
;;
esac
Zero-indexed, one-dimensional arrays. No index bound checks.
arr=("apple" "ball" "cat" "dog") # create, notice no commas ","
arr[1]=bear # modify an element
echo ${arr[3]} # access an element
unset arr[1] # remove an element (make it empty value)
echo ${arr[*]} # print all elements
echo ${arr[@]} # print all elements
echo ${arr[@]:2:6} # print range of elements (slicing)
echo ${#arr[*]} # print number of elements in array) (can also use -> #arr[@])
arr=(`cat`) # take input from terminal and create array; if they're newline separated, that'll also work
Curly braces {} are important to avoid variable name parsing ambiguity. Ex - $arr[1] (treats arr as a normal variable which is empty)
String Slicing: Strings can be sliced just like arrays:
str="JohnDoe"
echo ${str:0:4} # prints "John"
# on a static list
for i in 1 2 3 4 5
do
echo "Loop is on : $i"
done
# on a range
for i in {1..10} # {1..10..2} to jump by 2 steps
do
echo "Loop is on : $i"
done
# on a command's output
for i in `ls`
do
echo "Loop is on : $i"
done
# C-like for loop syntax
for ((i=0; i<5; i++))
do
echo "Loop is on : $i"
done
while [ ... ]
do
done
until [ ... ]
do
done
Loop Controls:
break
continue
exit
# specify a <n> parameter to break/continue out of n enclosing loops -> break n
# specify an exit_code parameter to exit command -> exit 0, for successful termination
function foo(){ }
foo(){ }
foo # call
foo(){
echo "$1 $2 is the best!"
#parameters can be accessed using $1, $2 and so on inside the function
return 69
}
foo john doe # parameterized function call
echo $? # capture value returned by last command (i.e. 69)
Local variables:
changeName(){
name = $1 # line 1
echo "Name changed to: $name"
}
name = "Morty"
echo "Name before is: $name"
changeName Rick
echo "Name after is: $name" # line 2
# line 2 will print "Rick"
# the variable "name" on line 1 is not local, to make it local see below:
local name = $1
# line 2 will print "Morty" if we use this as line 1
# from terminal
$ bash -x ./test.sh
# editing source sha-bang
#!/bin/sh -x
set -x
# debug only a portion of the script
set +x
| Notation | Description | Used with | Usage Example |
|---|---|---|---|
${} |
Variable name replacement with its value, inside it | variables | ${realm}heim |
$() |
Command substitution - literally execute commands inside it (alt of ``) | commands | $(ls -la) |
[ ] |
Test (use only -lt symbols inside) (spaces matter) |
if, etc… |
if [ 5 -gt 1 ] |
[[ ]] |
Test (newer & safer) (use both -lt and < symbols inside) (spaces matter) |
same as above | if [[ 5 > 1 ]] or if [[ 5 -gt 1 ]] |
(( )) |
Test - returns 0 or 1 after arithmetic evaluation, can’t test Strings inside this |
same as above | if (( 5+1 )) |
$(( )) |
Arithmetic evaluation and substitution, returns evaluated value | arithmetic expressions | sum=$(( 5+1 )) |