Bash Scripting

A very brief introduction on its usage

March 14, 2022 · 21 mins read

Bash Shell Scripting

Bash is a command language interpreter. It is the default command interpreter on most GNU/Linux systems. The name is an acronym for the ‘Bourne-Again SHell’.

Scripting allows for automatic command execution that would otherwise take many manually executed interactive steps.

Bash aliases

  • shortcuts to bash commands
  • syntax
	alias name="command"

Intro to bash shell scripting

  • The check with shell
	echo $SHELL
		OR
	echo $0
  • To list all available shells
	>~ cat /etc/shells
	# Pathnames of valid login shells.
	# See shells(5) for details.

	/bin/sh
	/bin/bash
	/usr/bin/git-shell
	/bin/zsh
	/usr/bin/zsh
  • Basic Shell Script anatomy
	#!/bin/bash for bash
	# Always starts with a shebang
	# This is a comment
	echo "Hello, World!"
  • For python the shebang will be #!/usr/bin/python

Running scripts

  • Make the script executable
    • chmod +x script_name
    • chmod 700 script_name
  • Run the script with
    • bash script_name.sh (runs without execute permission)
    • ./script_name.sh
    • For python python3 script_name.sh (runs without execute permission)
    • source script_name.sh (execute permission is not required)
    • ./ runs the script in sub shell and calling it runs in current shell
    • when run by calling a program binary, it overrides the shebang

Variables in Bash

  • A name for a memory location
  • Anatomy : name_of_variable assignment_operator value_of_variable
  • os=Linux os = Linux is not allowed : no spaces
  • if there is a space in variable value, enclose it with ""
 os="Debian Bullseye"
  • Bash is a weakly typed, no type declaration
  • In bash, numbers are only integers no floating points numbers
    • version=11
  • variable names cannot start with a
    • space
    • number
    • Cannot have
      • . in a name
      • @ in a name
  • variable names can however have with _
    • This is okay: server_name:”Apache 2.4”
  • the value of the variable can be referenced using $variable_name

    • echo $os
    • echo "I am learning $os"
    • Note: While referencing variable in string, make sure to use "" and not ''
      	> echo "I am learning $os"
      	I amlearning Linux
      
  • NOTICE THE DIFFERENCE with "" and ''

    	> echo 'I am learning $os'
    	I am learning $os
    
  • \$ can be used as an escape character to reference variable names instead of variable values.
    	> echo "The value of \$os is $os"
    	The value of $os is Linux
    
  • Use keyword set to list all the shell variables and pipe it to less or grep depending on the use case.
  • Use keyword unset to remove a variable
  • good practice to keep user defined variables names in lower case letters so there is no conflict with env variables
  • use declare keyword with -r flag to create a read only variable
    	declare -r logdir="/var/log"
    
    • use case
        ls $logdir
      
    • reassignment is not allowed
        > logdir=abc
        bash: logdir: readonly variable
      

Environment Variables

Each time we launch a terminal window and a shell inside it, a collection of predefined variables are set with dynamic values which are used to customize how a system works. There are two class of such variables

  • Environment Variables
    • defined for the current shell
    • inherited by a child shells or processes
    • used to pass information to processes
    • displayed using env or print env
    • Some built in environment variables
      • $PATH : path variable
      • $USER : Current logged in user
      • $HOME : Home directory of $USER
      • $HISTFILE : Shell command history file
      • $HISTSIZE : Max length of $HISTFILE
    • The names are UPPERCASE
    • To create an new env variable, export it in a startup config like .bashrc
    • To and to path: export PATH=$PATH:/path_to_dir/
    • To add a new: export MYVAR=value
  • Shell Variables

    • contained exclusively within the shell in which they were set or defined
    • displayed using set
  • Configuration
    • user: .bashrc
    • system-wide: /etc/bash.bashrc and /etc/profile

Getting user Input

  • Keyword read: read from standard input into shell variables
	> read name
	enter_name_here
  • A variable named name is created with the input value
  • use -p flag to echo a prompt or hint
	> read -p "Enter your name please: " name
	enter_name_here
  • Access name by $name like echo $name
  • use -s to hide the user input, for example a password.

Special Variables & Positional Arguments

  • Context

    • In apt install neofetch, install is the first argument in the script and neofetch is the second argument.
    • They are separated by a space referred to as inter field separators.
  • Example: In ./script.sh filename1 dir1 some_value other_value

    • $0 is the name of the script : script.sh
    • $1 is the first positional argument : filename1
    • $2 is the second positional argument : dir1
    • $4 is the last positional argument : other_value
    • $9 would be the 9th positional argument and so on
    • $# is the number of positional arguments
    • ”$*“ is the string representation of all positional arguments
    • $? is the most recent foreground command exit status

Flow Control

Logic Statements

If, Elif and Else Statements

  • Anatomy of an if, elif, else statement
    • NOTE: double [[ ]] is a newer safer way to do test conditions as it works well with strings with spaces and allows for regex matching. But it also works with []
	if [[ this_condition_is_true ]]
	then
		execute_this_code
	elif [[ some_ther_condition_is_true ]]
	then
		execute_this_code
	else
		execute_this_code
	fi
  • The man page for test contains all the test condition allowed in an if statement
  • check the man page for more.
  • Some common conditions
    • -f is a file
    • -d is a directory

Testing conditions for numbers

  • Some common testing conditions for numbers
  INTEGER1 -eq INTEGER2
    INTEGER1 is equal to INTEGER2

  INTEGER1 -ge INTEGER2
    INTEGER1 is greater than or equal to INTEGER2

  INTEGER1 -gt INTEGER2
    INTEGER1 is greater than INTEGER2

  INTEGER1 -le INTEGER2
    INTEGER1 is less than or equal to INTEGER2

  INTEGER1 -lt INTEGER2
    INTEGER1 is less than INTEGER2

  INTEGER1 -ne INTEGER2
    INTEGER1 is not equal to INTEGER2
  • Example
	#!/bin/bash
	read -p "Enter and number: " guess
	if [[ $guess -eq 5 ]]
	then
		echo "Jackpot"
	else
		echo "Okay looser!"
	fi
  • Some testing operations for strings

  -n STRING
    the length of STRING is nonzero

  STRING equivalent to -n STRING

  -z STRING
    the length of STRING is zero

  STRING1 = STRING2
    the strings are equal

  STRING1 != STRING2
    the strings are not equal

  • Example
	#!/bin/bash
	read -p "String1: " str1
	read -p "String2: " str2

	########################
	# Method 1
	########################
	if [ "$str1" = "$str2" ]
	then
		echo "Equal strings"
	else
		echo "Not Equal strings"
	if
	########################
	########################
	# Method 2
	########################
	if [[ "$str1" == "$str2" ]]
	then
		echo "Equal strings"
	else
		echo "Not Equal strings"
	if
	########################

  • Example: Sub strings
	str1="some strings with linux in it."

	if [[ '$str1' == *"linux"* ]]
	then
		echo "The substring linux IS present."
	else
		echo "The substring linux IS NOT present."
	fi

Multiple testing conditions and nested if statements

  • For multiple conditions

    • the logical or and and operators can be used
      • or is ||
      • and is &&
    • syntax
    • [[ condition 1 ]] && [[ condition 2 ]]
    • [[ condition 1 ]] || [[ condition 2 ]]
    • Alternatively
    EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true
    
    EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true
    
  • Anatomy of a nested if statement

	if [[ condition ]]
	then
		if [[ condition 2]]
		then
			do_this
		else
			do_this
	else
		do_this
	fi

For loop (Loops)

  • Anatomy of a for loop
	for item in LIST
	  do
	    COMMANDS
	done
  • Example: Iterate through a list
	#!/bin/bash
	for os in Ubuntu CentOS Slackware Kali "MX Linux"
	do
	  echo "OS is $os"
	done
  • Example: Iterate through a range of numbers
    • Specify range {start_num..end_num}
    • with step {start_num..end_num..step
	for num in {3..7}
	do
	  echo "num is $num"
	done
  • Example: Loop through items in a directory.
	for item in ./*
	do
	  if [[ -f "$item" ]]
	  then
		echo "$item"
	  fi
	done

While loop (Loops)

  • Anatomy of a while loop.
  • It runs as long as a specific command is true
	while CONDITION_IS_TRUE
	do
		COMMANDS
	done
  • Example: Iterate through numbers
	i=0

	while [[ $i -lt 10]]
	do
		echo "i: $i"
		((i++))
	done
  • Example: Infinite loop
	#!/bin/bash
	while true
	do
	  echo "In an infinite loop, press CTRL+C to exit."
	  sleep 1
	done

  • alternates
    • while [[ true ]]
    • while :
    • while true; do echo "infinite loop"; sleep 1; done

Command Substitutions

  • To store the output of a command into a variable
	# Method 1
	var_one=`command`

	# Method 2
	var_two="$(command)"

  • For arithmetic operations use
    • ((arithmetic_operation))

Case Statements

  • Its a simpler alternative to if,elif,else
  • It is similar to switch statement in C, C++ & Java
  • Anatomy of the a case statement
    • PATTERN can contain special characters
    • STATEMENT blocks end with ;; (double semicolon)
    • * PATTERN is a default case
	case EXPRESSION in
		PATTERN_1)
			STATEMENTS
			;;
		PATTERN_2)
			STATEMENTS
			;;
		PATTERN_3)
			STATEMENTS
			;;
		PATTERN_4)
			STATEMENTS
			;;
		""
		""
		*)
			STATEMENTS
			;;
	esac

  • Example
    • Multiple options in case option_1|option_2
    • Enclose "A String With Spaces" inside ""
	#!/bin/bash

	echo -n "Enter your favorite pet: "
	read PET

	case "$PET" in
		dog)
			echo "Your favorite pet is the dog."
			;;
		cat|Cat)
			echo "You like cats."
			;;
		fish|"African Turtle")
			echo "Fish or turtles are great."
			;;
		*)
			echo "Your favorite pet is weird"
	esac
  • Example: Managing processes signal.sh
	#!/bin/bash
	if [[ $# -ne 2 ]]
	then
	  echo "Run the script with 2 arguments: Signal and PID."
	  exit
	fi

	case "$1" in
	  1)
		echo "Sending the SIGHUP signal to $2"
		kill -SIGHUP $2
		;;

	  2)
		echo "Sending the SIGINT signal to $2"
		kill -SIGINT $2
		;;
	  15)
		echo "Sending the SIGTERM signal to $2"
		kill -15 $2
		;;
	  *)
		echo "Signal number $1 will not be delivered"
	esac

Functions in Bash

  • Used to organize code
    • modularity
    • reusability
  • Compared to other programming languages, bash functions are limited.
  • Anatomy of a bash function
    • the () in functions definitions are purely decorative
      • they do not take any arguments in them
    • the arguments can be passed into functions similar to command line arguments, i.e., refer to the variable or arguments with $varibale_name or $1 $2 etc.
    • bash functions do not allow a return value
	#!/bin/bash
	function name_of_function () {
		one thing to do
		two thing to do
	}

	OR

	name_of_function () {
		do_something
	}

	# This is a function call
	name_of_function

Variable scope in function

  • By default in bash all variables are global
  • To declare a local variable inside a function, use the keyword local
	var1-"AA"
	var2="BB"

	func1() {
		var1="XX"
		local var2="YY"
		echo "Inside func1: var1=$var1, var2=$var2"
	}

	func1
	echo "Outside func1: var1=$var1, var2=$var2"
  • Output from above

	Inside func1: var1=XX, var2=YY
	Outside func1: var1=XX, var2=BB

  • Anatomy of a select statement
    • ITEM is a user defined variable
    • LIST is a series of strings or output of commands
      • if a "string contains a space" enclose it in ""
    • The selection is stored in a predefined variable REPLY
    • The default prompt is #? and can be change by setting a predefined variable PS3
	select ITEM in LIST
	do
		COMMANDS
	done
  • Example
	#!/bin/bash
	PS3="Choose your country"
	select COUNTRY in India Germany France USA "United Kingdom" Quit
	do
	  case $REPLY in
		1)
		  echo "You speak many languages"
		  ;;
		2)
		  echo "You speak many languages"
		  ;;
		3)
		  echo "You speak many languages"
		  ;;
		4)
		  echo "You just speak amarican english"
		  ;;
		5)
		  echo "You speak many languages"
		6)
		  echo "Qutting..."
		  break
		  ;;
		*)
		  echo "Invalid Option"
	done

Scripts for reference

Github Repo: bass-scripting-basics