Skip to content

Latest commit

 

History

History
337 lines (262 loc) · 9.36 KB

bash_scripting.md

File metadata and controls

337 lines (262 loc) · 9.36 KB

Bash Scripting

Curator: Taavi Kivisik

Mainly based on:

Absolute Basics

Simplest Bash Script

When you save the following code as hello_world.sh

#!/usr/bin/env bash
echo "Hello World!"

Shebang

The very first line of a script file which starts with a #! is called a shebang or hashbang. It tells the operating system where to find a program (bash) which should be used to run the script file.

Often people also use an absolute path to bash, but that is discouraged due to the different locations where bash could be installed to in different distributions.

#!/bin/bash
echo "Hello World!"

Running a Bash Script

Type bash followed by your script name (hello_world.sh)

$ bash "hello_world.sh"

General Form of a Bash Command

[variable_assignment] command [flags] [arguments]

# Execute a command without arguments
$ ls
# Execute a command with an argument (/bin) - show files in /bin directory
$ ls /bin
# Execute a command with flags (-lht) and an argument (/bin) - list files as a list (-l), show human readable sizes (-h), order by modification time (-t)
$ ls -lht /bin
# Execute a command with specified environmen variables
$ size=5 multiplication_table.sh

Variable Assignment and Use

Variable assignment is done so you type the variable name followed by an equal sign followed by the value. There must not be space between the variable name and other parts, otherwise Bash considers it a command with arguments. You can use a variable by typing a dollar sign ($) immediately before the variable name.
Use lower-case letters for naming your variables. Variable names written in capital letters are meant for system variables.

#!/usr/bin/env bash
a=1
b=2
echo "a is $a and b is $b"        # output: 'a is 1 and b is 2'
echo "$a + $b = $[ $a + $b ]"   # output: '1 + 2 = 3'

Some special variables:

  • $$ - process id (PID) of a current script
  • $! - PID of a last run script
  • $USER - user running this script
  • $HOSTNAME - hostname of the machine

Apostrophes and Quotes

Meaningful Difference between Apostrophes and Quotes

It's better to form a habit of using apostrophes and quotes. They rarely break something, but often save from errors or mistakes. Text inside apostrophes (i.e. single quotes (')) will remain literal even with variables in it. Variables inside quotes (i.e. double quotes (")) will be expanded.

#!/usr/bin/env bash
literally='will not be expanded'
non_literally='so that variables are expanded'

$ echo 'This will be printed $literally'
# output: This will be printed $literally

$ echo "This will be written $non_literally"
# output: This will be printed so that variables are expanded

Using Both the Apostrophes and Quotes

Sometimes there is a need to use them both at the same time.

#!/usr/bin/env bash

a=1
$ echo '$a'
# output: $a
$ echo "$a"
# output: 1

$ echo '$a"$a"'     # Outermost single quotes set the stage for being interpreted literally
# output: $a"$a"
$ echo "$a'$a'"     # Outermost double quotes set the stage for expansion
# output: 1'1'

Escaping Apostrophes and Quotes

There are two ways to escape apostrophes and quotes (Also see this Thread in Stack Overflow):

  • Method 1: echo 'It''s an amazing quote'
  • Method 2: echo 'It'"'"s an amazing quote'

They both work by first ending the engoing quote environment followed by an escaped quote, followed by a continuation of the quote environment. Method one does it by escaping the quote using a backslash. Method two does it by escaping the quote using double quotes.

#!/usr/bin/env bash

a=1
$ echo 'I like {$a}s'
# output: I like {$a}s
$ echo "I like $a's"
# output: I like 1's

# Examples of the first escape method
$ echo 'I like both $a'\''s and the "$a"'
# output: I like both $a's and the "$a"
$ echo "I like both $a's and the "\""$a"\"
# output: I like both 1's and the "1"

# Examples of the second escape method
$ echo 'I like both $a'"'"'s and the "$a"'
# output: I like both $a's and the "$a"
$ echo "I like both $a's and the "'"'"$a"'"'", which is nice"
# output: I like both 1's and the "1", which is nice

Conditionals

If Statement

Here is a list of logical operators for comparing integers and strings: use of logical operators

#!/usr/bin/env bash
a=1
if [ $a -eq 1 ]; then
    echo "a is equal to 1"
elif [ $a -lt 1 ]; then
    echo "a is less than 1"
else
    echo "a is greater than 1"
fi

When a command is finished successfully, it returns the exit code 0. Unsuccessful termination of a program returns another number, which is associated with an error type.

Parentheses, Brackets, Braces

  • Parentheses '( )'
    • start a subshell. Current shell's environment is passed on to the child shell.
    • are used to build an array.
  • Double Parentheses '(( ))' are used for integer arithmetic.
  • Brackets '[' are a builtin test command.
  • Double Brackets '[[' are an extension builtin for brackets. It allows for more readable code.
  • Braces '{ }' are used for
    • grouping commands together in a current shell (need to end with a semicolon).
    • variable substitution
    • brace expansion (e.g. {1..5..2} and {a..j})
# Execute commands in a subshell
$ parent_time=$( date +%H:%M:%S )
$ echo Parent shell: $parent_time and child shell: $( sleep 1 ; date +%H:%M:%S ; echo Child shell prints this )
Parent shell: 17:15:08 and child shell: 17:15:09 Child shell prints this

# Array initialization
$ array=(1 2 3 4 5)
$ echo "${array[0]}     # Prints the first element
1
$ echo "$(( 1 + 1 ))"
2
$ if [ "1" -lt "2" ] ; then echo 'One is truly less than two' ; fi
One is truly less than two
$ if [ -d "directory_name" ] ; then echo 'That directory exists' ; fi
$ if [[ "1" < "2" ]] ; then echo 'Double brackets allow for the use of the less than symbol' ; fi
Double brackets allow for the use of the less than symbol
# Group commands together in a current shell
$ { echo this ; echo and ; echo that ; }
this
and
that
$ a='great'
$ echo "${a}est"    # without braces, it would look for 'aest' variable
greatest

# print every third element starting from 1 until 10
$ echo {1..10..3} 
1 4 7 10

# print every other character from a to j
$ echo {a..j..2}
a c e g i

[[ and (( are commands which take a list of arguments as input, where the last argument must be ]] or )) respectively. If whatever is between [[ and ]] evaluates to true, then the first part after && is run, otherwise the second.

#!/usr/bin/env bash
a=1
b=2
c=1
echo "a = $a, b = $b, c = $c"
[[ $a = $b ]] && echo "a is equal to b" || echo "a is not equal to b"
# output: a is not equal to b
[[ $a = $c ]] && echo "a is equal to c" || echo "a is not equal to c"
# output: a is equal to c
[[ $b = $c ]] && echo "b is equal to c" || echo "b is not equal to c"
# output: b is not equal to c

Looping

For Loop

  • Bash doesn't require indenting what's inside the loop, but it aids readability.
  • Semicolon is the same as a newline. Therefore you could also drop the semicolon and move the do to the next line.

Examples:

#!/usr/bin/env bash
for i in 1 2 3; do
    echo "$i"
done
#!/usr/bin/env bash
for i in seq{1..5}; do
    echo "$i"
done
#!/usr/bin/env bash
size=10
# for ((initial i ; loop until condition ; after each loop, increase the value of i)); do
for ((i=1 ; i<=$size ; i++)); do
    echo "$i"
done

Example which produces a multiplication table:

#!/usr/bin/env bash
for i in {1..10}; do
    for j in 1 2 3 4 5 6 7 8 9 10
    do
        printf "%4s" $(( $i * $j )) 
    done
    echo
done

While Loop

#!/usr/bin/env bash
i=1
while [ $i -lt 6 ]; do
    echo "$i"
    i=$[ $i + 1 ]
done

Functions

  • $# - represents the number of arguments passed to the program (outside of a function) or to the function (inside of a function).
  • $@ - represents the arguments passed to the program (outside of a function) or to the function (inside of a function).
  • $? - exit status of a most recently ran process.
#!/usr/bin/env bash
argument_counter () {
    echo "Function argument count: $#"
    echo "Function arguments: $@"
    echo "Function's First argument was: $1"
}

echo "Script is the argument number 0: $0"
echo "Program argument count: $#" 
echo "Program arguments : $@"
# Pass all program arguments to the function, and add the first,
# second and first again argument once again.
argument_counter $@ $1 $2 $1

Other Useful Expressions

<<

It's called the 'here-document' structure and can be used to create a multiline document on the go.

#!/usr/bin/env bash

# Creates a file named 'output_file.txt' with a content of anything between a specified end pattern (i.e. EOF in the following case)
cat > output_file.txt << EOF
first line
second line
EOF

# Counts the lines, words, characters of the input provided by the here-document structure.
wc << ABC
first line
second line
ABC

Example Bash Scripts

See scripting_examples folder for Bash script examples. E.g.

  • Random Number Guessing Game
  • Multiplication table
  • Argument reverser function