Bash Script - 101
Introduction
Bourne-Again SHell (bash) is a Unix shell first released in 1989 and version 4.0 was released in 2009. Command that can be run in bash shell can also be saved as file and run as bash script.
Getting Help
Bash has very compact help system bundled with it and can be accessed via:
$ helpor if running another shell (zsh, fish):
$ bash -c helpFor finding a command type bash builtin command type is very helpful. For finding all instance of a command:
$ type -a <command-name>
$ type -a cd # Output: cd is a shell builtinIf its a shell builtin then detailed information of builtin commands can be found using
$ help <command-name>echo builtin command is used for printing arguments to standard output. Each arguments is printed with space in
between. If any argument is variable (starts with $) prints value of the variable.
$ echo Hello World # Output: Hello World
$ echo Hello World # Output: Hello World
# $BASH_VERSION is a builtin variable, prints the running bash version
$ echo Hello $BASH_VERSION # Output: Hello 4.4.19(1)-releaseBy default echo appends a newline in the end, -n option omits newline.
# Multiple statements can be written in a line using ;
$ echo -n Hello ; echo World # Output: HelloWorldArguments can be surrounded with double (“) or single (‘) quotes. Variable don’t substitutes if single quotes (‘) is used.
$ echo "Hello World" # Output: Hello World
$ echo "Bash $BASH_VERSION" # Output: Bash 4.4.19(1)-release
$ echo 'Bash $BASH_VERSION' # Output: Bash $BASH_VERSIONBy default echo doesn’t interprets backslash characters. -e options enables interpretation. See help echo for
complete list of backslash characters.
$ echo "Hello\tWorld\n" # Output: Hello\tWorld\n
$ echo -e "Hello\tWorld\n" # Output: Hello WorldCreating a Bash Script
For ease of usage and avoiding repeatability command can be written in file and that file can be run as command. File
name can be any valid unix filename, usually .sh extension is appended although not requited. In bash # is used for
line comment, but when first line starts with #! followed by a command path, that command is used when the file is run
as executable.
#/bin/bash
echo Hello WorldRun Script
Easiest with to run a bash script is using bash executable. This will fork another shell and will run inside that
shell.
$ bash <file-name>For running in current bash session
$ source <file-name> # or . <file-name>If the file is executable and #!/bin/bash is defined, then the file can be run directly.
# Make a file executable
$ chomd +x <file-name>
# As first line of file is '#!/bin/bash' will be invoked like
# /bin/bash <file-name>
$ ./<file-name>IO Redirection
In bash, output of a command can be used as input for another command using |. If standard error is also needed |&
is used.
$ cat file-not-exists | wc -l
# Output:
# cat: file-not-exits: No such file or directory
# 0
$ cat file-not-exists |& cat -n # Output: 1In unix-like operating system three predefined unique open file or file desiccator is stdin (0), stdout (1) and stderr
(3). stdout of a command can be send to a file using > and using >> to append in file.
$ cat -n /etc/password 1> <file-name>
$ cat -n /etc/password > <file-name> # can omit as 1 is default for output
$ cat -n /etc/password >> <file-name> # appends to <file-name>
# For sending only stderr
$ cat -n /etc/password 2> <file-name>
$ cat -n /etc/password 2>> <file-name> # appends only stderr to <file-name>
# For sending both stdin and stderr
$ cat -n /etc/password > <file-name> 2>&1
$ cat -n /etc/password &> <file-name> # same as aboveFor reading from a file < is used.
$ cat -n 0< /etc/passwd
$ cat -n < /etc/passwd # can omit as 0 is default for inputVariable Declaration
Variable is declared using syntax <VARIABLE-NAME>=<VALUE>. There must be no space around =. Bash variable
names are case sensitive and can be named using uppercase and lowercase characters (not recommended), numbers,
underscore. Number can’t be first character. It’s good practice to always surround variable values with quotes. When
using the value of variable $ is prefixed with the name.
YESTERDAY=Friday
TODAY="Saturday"
echo $TODAY # Output: Saturday
echo ${TODAY} # same as above
MESSAGE="Load"
echo "Please wait ${MESSAGE}ing data." # Output: Please wait Loading data.
# Variable can also be expression value
DATE=$(date +%A)
# Arithmetic expression
FIVE=$(( 2 + 3 ))Input
Input can be taken using shell builtin read command. -p option is used for prompting text. When user input is
added its stored in given variable.
$ read -p "Enter Your Name: " INPUT_NAME
Enter Your Name: John Doe
$ echo $INPUT_NAME # Output: John DoeAnother options is passing as shell command argument, which can be accessed using positional arguments $1 - $9. $#
is a total argument count. $0 is running script name.
For the following script,
#!/bin/bash
# Filename: pos-arg.sh
echo "$# arguments; first: $1, second: $2"Output will depends on how the script is invoked.
$ ./pos-arg.sh one two # Output: 2 arguments; first: one, second: two
$ ./pos-arg.sh one two three # Output: 3 arguments; first: one, second: twoControl Statements
Command can also be executed based on condition using if/else. [[ ... ]] is conditional command that returns 0 or 1
based on expression.
DATE="Sun"
# Output: Yay! Weekend
if [[ "$DATE" = "Sun" ]]; then
echo "Yay! Weekend"
elif [[ "$DATE" = "Tue" ]]; then
echo "Work Hard"
else
echo "Boring"
fiFor executing command on every member of list, loop is used. Bash has for, while, until loop. Bash has two
flavour of for loop
# Output 12345
for (( NUM=1; NUM <= 5; NUM++)); do
echo -n $NUM
done
# Output 12345
for NUM in 1 2 3 4 5; do
echo -n $NUM
done
# Same as above, using 'seq'
# Output 12345
for NUM in $(seq 1 5); do
echo -n $NUM
done
# Prints all files in /etc
for FILE in /etc/*; do
echo $FILE
done
# If called like ./for-test.sh one two three
# Output: one, two, three,
for ARG in "$@"; do
echo $ARG
done
# If called like ./for-test.sh one two three
# Output: one two three,
for ARG in "$*"; do
echo $ARG
doneBash while loop can be used effectively when how many times the loop needs to executes is undefined. It will execute while certain condition is met.
# Output: 54321
NUM=5
while [[ "$NUM" -gt 0 ]]; do
echo -n $NUM
NUM=$(( NUM - 1 ))
done
# If called with ./while-test.sh one two three
# Output: one, two, three,
while [[ "$#" -ne 0 ]]; do
echo -n "$1, "
shift # after shift $1 is removed and $2 becames $1
done
# If INPUT value is not gives, continues to ask
INPUT=""
while [[ "$INPUT" = "" ]]; do
read -p "Enter Name: " INPUT
doneCase statements executes command based on patten matching.
DAY="sun"
# Output: Yay! Weekend
case "$DAY" in
[Ss]un) echo "Yay! Weekend" ;;
[Tt]ue) echo "Work Hard" ;;
*) echo "Boring" ;;
esacFunction
For code maintenance and readability function is a very important feature of any language. Bash function can be defined
with optional function keyword.
# Defined with 'function' keyword
function func_one() {
echo "Func One"
}
# Defined without 'function' keyword, recommend style
func_two() {
echo "Func Two"
}
# Arguments can be accessed $1 - $9
# But $0 prints the script name
func_with_argument() {
echo "Func with Arguments"
echo "Count: $#, values: $1, $2"
}
# Function can return exit status
# if no return, then exit status of last command is used
func_with_return() {
echo "Before return"
if [[ true ]]; then
return
fi
echo "After return"
}
func_one # Output: Func One
func_two # Output: Func Two
func_with_argument One Two
# Output:
# Func with Arguments
# Count: 2, value: One, Two
func_with_return # Output: Before returnTrap
Bash has a builtin, named trap which can be used to run command when certain event occurs. One of these events is
EXIT which can be used to run some command when script ends. This can be used to clean or free up resources. Lets
consider a script.
TEMP_FILE_NAME="/tmp/app"
cleanup () {
echo "Removing $TEMP_FILE_NAME"
rm "$TEMP_FILE_NAME"
}
trap cleanup EXIT
echo "Create $TEMP_FILE_NAME"
touch "$TEMP_FILE_NAME"
for i in 1 2 3; do
echo "Add $i in $TEMP_FILE_NAME"
echo "$i" >> "$TEMP_FILE_NAME"
sleep 10
done
# Output
# Create /tmp/app
# Add 1 in /tmp/app
# Add 2 in /tmp/app
# Add 3 in /tmp/app
# Removing /tmp/appcleanup will also be called if an unfinished running script is killed or ended using CTRL-C.
Tools Version
- bash 4.4.19(1)-release