Introduction to Bash Scripting

https://forge.uclouvain.be/barriat/learning-bash

October 09, 2025
CISM/CÉCI Training Sessions

Linux command line

A Linux terminal is where you enter Linux commands

It's called the Command Line User Interface

CLUI (or just CLI) is one of the many strengths of Linux :

  • allows to be independent of distros (or UNIX systems like OSX)
  • allows to easily work remotely (SSH)
  • allows to join together simple (and less simple) commands to do complex things and automate = scripting

In Linux, process automation relies heavily on scripting. This involves creating a file containing a series of commands that can be executed together.

10/09/2025 | Introduction to Bash Scripting

Linux Shell

A shell is a program that takes commands from the keyboard and transmits them to the operating system to perform

The main function is to interpret your commands = language

Shells have some built-in commands

A shell also supports programming constructs, allowing complex commands to be built from smaller parts = scripts

Scripts can be saved as files to become new commands

many commands on a typical Linux system are scripts

10/09/2025 | Introduction to Bash Scripting

Bash

The Bash shell is one of several shells available for Linux

It is the default command interpreter on most GNU/Linux systems. The name is an acronym for the "Bourne-Again SHell"

Bash Scripting Demo

#!/bin/bash

# declare STRING variable
STRING="Hello World"

# print variable on a screen
echo $STRING
10/09/2025 | Introduction to Bash Scripting

Bash environment

In a Bash shell many things constitute your environment

  • the form of your 'prompt' (what comes left of your commands)
  • your home directory and your working directory
  • the name of your shell
  • functions that you have defined
  • etc.

Environment includes many variables that may have been set by bash or by you

10/09/2025 | Introduction to Bash Scripting

Environment variables

Variables
USER the name of the logged-in user
HOME the user's home directory (similar to ~ )
PWD the current working directory
UID the numeric user id of the logged-in user

Access the value of a variable by prefixing its name with $

So to get the value of USER you would use $USER in bash code

You can use special files to control bash variables : $HOME/.bashrc

10/09/2025 | Introduction to Bash Scripting

Bash Scripting basics

By naming convention, bash scripts end with .sh

however, bash scripts can run perfectly fine without any extension

A good practice is to define a shebang : first line of the script
shebang is simply an absolute path to the shell interpreter

combination of bash # and bang !

The usual shebang for bash is #!/bin/bash

10/09/2025 | Introduction to Bash Scripting

Comments start with #

On a line, any characters after # will be ignored (with the exception of #!)

echo "A comment will follow." # Comment here.
#                            ^ Note whitespace before #

There is no standard indentation

  • Pick a standard in your team that you can all work to
  • Use something your editor makes easy (Vim uses Tab)
10/09/2025 | Introduction to Bash Scripting

Permissions and execution

  • Bash script is nothing else than a text file containing instructions to be executed sequentially

    by default in Linux, a new text file's permissons are -rw-r--r-- (or 644)

  • You can run the script hello_world.sh using
    • sh hello_world.sh
    • bash hello_world.sh
    • chmod u+x run_all.sh then ./hello_world.sh

      after the chmod, you file is -rwxr--r-- (or 744)

10/09/2025 | Introduction to Bash Scripting

Hands-on exercise

Your first bash script:

  1. create a folder bash_exercises and go there
  2. use your favourite editor (vim, obviously) to create a new file called exercise_1.sh
  3. write some code in it to display the current working directory as:

    The current directory is : /home/myself/bash_exercises

  4. make the file executable
  5. run it !
10/09/2025 | Introduction to Bash Scripting

Variables and data types in Bash

Variables let you store data : numeric values or character(s)

You can use variables to read, access, and manipulate data throughout your script

You don't specify data types in Bash

  • assign directly : greeting="Welcome" or a=4
  • assign based on variable: b=$a

And then access using $: echo $greeting

!!! no space before or after = in the assignation !!!
myvar = "Hello World" 💥

10/09/2025 | Introduction to Bash Scripting

Quotes for character(s) " '

Double will do variable substitution, single will not:

$ echo "my home is $HOME" 
my home is /home/myself
$ echo 'my home is $HOME'
my home is $HOME

Command Substitution

#!/bin/bash
# Save the output of a command into a variable
myvar=$( ls )
10/09/2025 | Introduction to Bash Scripting

Variable naming conventions

  • Variable names should start with a letter or an underscore

  • Variable names can contain letters, numbers, and underscores

  • Variable names should not contain spaces or special characters

  • Variable names are case-sensitive

  • Use descriptive names that reflect the purpose of the variable

  • Avoid using reserved keywords, such as if, then, else, fi, and so on...

  • Never name your private variables using only UPPERCASE characters to avoid conflicts with builtins

10/09/2025 | Introduction to Bash Scripting

String manipulation

Consider filename=/var/log/messages.tar.gz

  • substring removal from left :
    • ${filename##/var} is /log/messages.tar.gz
  • substring removal from right :
    • ${filename%%.gz} is /var/log/messages.tar

You can use * to match all characters:

  • ${filename%%.*} is /var/log/messages
  • $(filename##*/) is messages.tar.gz

How to return the length of a variable ? ${#filename} is 24

10/09/2025 | Introduction to Bash Scripting

Arithmetic

Operator Operation
+ - \* / addition, subtraction, multiplication, division
var++ increase the variable var by 1
var-- decrease the variable var by 1
% modulus (remainder after division)
10/09/2025 | Introduction to Bash Scripting

Arithmetic

Use double parentheses (❗ integers only ❗)

a=$(( 4 * 5 ))
b=$(( $a + 4 ))
echo $b # 24

# $ is optional inside parentheses

b=$(( a - 3 ))
echo $b # 17

(( b++ ))
(( b += 3 ))
echo $b # 21
10/09/2025 | Introduction to Bash Scripting

Conditional statements

Use:

  • if condition; then to start conditional block
  • elif condition; then to start alternative condition block
  • else to start alternative block
  • fi to close conditional block

The following operators can be used beween conditions:

  • || means OR
  • && mean AND
10/09/2025 | Introduction to Bash Scripting

Conditional syntax

if [[ $num -gt 5 && $num -le 7 ]]; then

  echo '$num is 6 or 7'

elif [[ $num -lt 0 || $num -eq 0 ]]; then

  echo '$num is negative or zero'

else

  echo '$num is positive (but not 6, 7 or zero)'

fi
10/09/2025 | Introduction to Bash Scripting

Conditions with numbers

Operator Description
! EXPRESSION The EXPRESSION is false
INT1 -eq INT2 INTEGER1 is equal to INTEGER2 (or ==)
INT1 -ne INT2 INTEGER1 is different from INTEGER2
INT1 -gt/-ge INT2 INTEGER1 is higher / higher or equal to INTEGER2
INT1 -lt/-le INT2 INTEGER1 is lower / lower or equal to INTEGER2

Note: Do not use signs like > : they compare strings only

10/09/2025 | Introduction to Bash Scripting

Conditions with strings

Operator Description
-n STRING The length of STRING is greater than zero
-z STRING The lengh of STRING is zero (ie it is empty)
STR1 = STR2 STRING1 is equal to STRING2
STR1 != STR2 STRING1 is not equal to STRING2
10/09/2025 | Introduction to Bash Scripting

Conditions on files

Operator Description
-d FILE FILE exists and is a directory
-e FILE FILE exists
-s FILE FILE exists and is not empty
-r/-w/-x FILE FILE exists and user has read/write/execute permissions
if [[ -e "my_file.sh" ]]; then
  echo "my_file.sh exists"
fi
10/09/2025 | Introduction to Bash Scripting

Conditional with arithmetics

If your condition is only arithmetics / booleans you can use double brackets (just like we did for variables) :

if (( $num % 2 == 0 )); then
  echo "$num is an even number !"
fi

if (( $num % 2 == 0 && $num % 3 == 0 )); then
  echo "$num can be divided by 2 and 3"
fi
10/09/2025 | Introduction to Bash Scripting

Conditional Summary Table

Goal Syntax Notes
String test [[ $a == foo ]] for strings
File test [[ -f file.txt ]] for files
Integer math (( num % 2 == 0 )) for integers
Floating-point (( $(echo "$x > $y" | bc -l) )) uses bc
b=5.2
a=3.4
(( $(echo "$b > $a" | bc -l) )) && echo "b > a"
10/09/2025 | Introduction to Bash Scripting

Hands-on exercise

  1. In your bash_exercises folder create a new bash file called exercise_2.sh and make it executable

  2. Ask the user for two numbers smaller than 100 and put them in variables number1 and number2

    #!/bin/bash
    read number1
    read number2
    
  3. Check if the numbers are smaller than 100

    • If yes, check if both numbers are even and tell the user
    • If not, tell the user (use echo)
10/09/2025 | Introduction to Bash Scripting

Loops

Useful for automating repetitive tasks

Basic loop structures in Bash scripting :

  • while : perform a set of commands while a test is true

  • for : perform a set of commands for each item in a list

  • controlling loops

    • break : exit the currently running loop
    • continue : stop this iteration of the loop and begin the next iteration
10/09/2025 | Introduction to Bash Scripting

Examples

#!/bin/bash

# Basic while loop
counter=0
while [ $counter -lt 3 ]; do
  echo $counter
  ((counter++))
done
# range
for i in {1..5}
10/09/2025 | Introduction to Bash Scripting
# list of strings
words='Hello great world'
for word in $words
# range with steps for loop
for value in {10..0..-2}
# set of files
for file in $path/*.f90
# command result
for i in $( cat file.txt )
10/09/2025 | Introduction to Bash Scripting

Arrays

Indexed arrays

# Declare an array with 4 elements
my_array=( 'Debian Linux' 'Redhat Linux' Ubuntu OpenSUSE )
# get number of elements in the array
my_array_length=${#my_array[@]}

# Declare an empty array
my_array=( )
my_array[0]=56.45
my_array[1]=568
echo Number of elements: ${#my_array[@]}
# echo array's content
echo ${my_array[2]}
echo ${my_array[@]}
10/09/2025 | Introduction to Bash Scripting

Hands-on exercise

  1. In your bash_exercises folder create a new bash file called exercise_3.sh and make it executable
  2. Use the following list of words and put them together in an array:
    misplace discipline birthday lie classroom swallow casualty failure partner visible
  3. Register the start time with date +%N and put it in a variable tstart
  4. Loop over the words and ask the user to give the number of letters. Echo the answers.
  5. Register the end time in tend
  6. Display the total run time and the total number of letters.
10/09/2025 | Introduction to Bash Scripting

Arguments - Positional Parameters

How to pass command-line arguments to a bash script ?

#!/bin/bash
echo $1 $2 $4
echo $0
echo $#
echo $@
bash test_arg.sh a b c d e
a b d
test_arg.sh
5
a b c d e
10/09/2025 | Introduction to Bash Scripting
Special Variables
$0 the name of the script
$1 - $9 the first 9 arguments
$# how many arguments were passed
$@ all the arguments supplied
$$ the process ID of the current script
$? the exit status of the most recently run process
10/09/2025 | Introduction to Bash Scripting

Input/Output streams

Shells use 3 standard I/O streams

  • stdin is the standard input stream, which provides input to commands
  • stdout is the standard output stream, which displays output from commands
  • stderr is the standard error stream, which displays error output from commands

Shell has several meta-characters and control operators

10/09/2025 | Introduction to Bash Scripting

Control operators

Character Effect
; Normal separator between commands
&& Execute next command only if command succeeds
|| Execute next command only if command fails
& Don't wait for result of command before starting next command
| Use output of command as input for the next command
10/09/2025 | Introduction to Bash Scripting

Control operators

Character Effect
> file_desc Send standard output of command to file descriptor
>> file_desc Same but in append mode
< file_desc Use content of file descriptor as input
10/09/2025 | Introduction to Bash Scripting

Command separators

Commands can be combined using meta-characters and control operators

# cmd1; cmd2
$ cd myfolder; ls   # no matter cd to myfolder successfully, run ls

# cmd1 && cmd2
$ cd myfolder && ls  # run ls only after cd to myfolder

# cmd1 || cmd2
$ cd myfolder || ls  # if failed cd to myfolder, `ls` will run
10/09/2025 | Introduction to Bash Scripting

Redirections

Use the meta-character > in order to control the output streams stdout and stderr for a command or a bash script

From bash script

#!/bin/bash
#STDOUT to STDERR
echo "Redirect this STDOUT to STDERR" 1>&2 
#STDERR to STDOUT
cat $1 2>&1

Output streams to file(s)

./my_script.sh > STDOUT.log 2> STDERR.err
10/09/2025 | Introduction to Bash Scripting

How to Read a File Line By Line : input redirection

#!/bin/bash
# How to Read a File Line By Line
while IFS= read -r line; do
  echo "$line"
done

Then you can use:

./my_script.sh < file.txt

by default read removes all leading and trailing whitespace characters such as spaces and tabs

10/09/2025 | Introduction to Bash Scripting

Return codes

Linux command returns a status when it terminates normally or abnormally

  • every Linux command has an exit status
  • the exit status is an integer number
  • a command which exits with a 0 status has succeeded
  • a non-zero (1-255) exit status indicates failure

How do I display the exit status of shell command ?

date
echo $?
10/09/2025 | Introduction to Bash Scripting

How to store the exit status of the command in a shell variable ?

#!/bin/bash
date
status=$?
echo "The date command exit status : ${status}"

How to use the && and || operators with exit codes

command && echo "success"
command || echo "failed"
command && echo "success" || echo "failed"
10/09/2025 | Introduction to Bash Scripting

Hands-on exercise

  1. In your bash_exercises folder, copy exercise_3.sh to exercise_4.sh
  2. In this new file, loop over the words and write the number of letters of each word in a new file called output.txt
  3. Now loop over the created file output.txt to get the total number of letters
  4. Display the total run time and the total number of letters
10/09/2025 | Introduction to Bash Scripting

Functions

  • "small script within a script" that you may call multiple times
  • great way to reuse code
  • a function is most reuseable when it performs a single task
#!/bin/bash
hello_world () {
   echo 'hello, world'
}
hello_world

Functions must be declared before they are used

defining a function doesn't execute it

10/09/2025 | Introduction to Bash Scripting

Variables Scope

# Define bash global variable
# This variable is global and can be used anywhere in this bash script
var="global variable"

function my_function {
# Define my_function local variable
# This variable is local to my_function only
echo $var
local var="local variable"
echo $var
}

echo $var
my_function
# Note the bash global variable did not change
# "local" is my_function reserved word
echo $var
10/09/2025 | Introduction to Bash Scripting

Return an arbitrary value from a function

Assign the result of the function

my_function () {
  local func_result="some result"
  echo "$func_result"
}
func_result="$(my_function)"
echo $func_result
10/09/2025 | Introduction to Bash Scripting

Passing Arguments

In the same way than a bash script: see above ($1, $*, etc)

#!/bin/bash
print_something () {
    echo Hello $1
}
print_something Mars

❗ Athough it is possible, you should try to avoid having functions using the name of existing linux commands.

10/09/2025 | Introduction to Bash Scripting

Hands-on exercise

  1. Write a script called exercise_5.sh expecting 2 arguments.
  2. Write a function taking a folder path (e.g /home/ucl/elic/xxxx) and an extension (e.g py) as arguments
  3. Use the ls command to list the files in the given path having with the given extension. Write this list to a file called files_found.txt.
  4. Bonus : if there are no files, Exit with a non-zero error code
10/09/2025 | Introduction to Bash Scripting

Subshells

  • A subshell is a "child shell" spawned by the main shell ("parent shell")
  • A subshell is a separate instance of the command process, run as a new process
  • Unlike calling a shell script (slide before), subshells inherit the same variables as the original process
  • A subshell allows you to execute commands within a separate shell environment = Subshell Sandboxing

    useful to set temporary variables or change directories without affecting the parent shell's environment

  • Subshells can be used for parallel processing
10/09/2025 | Introduction to Bash Scripting

Shell vs Environment Variables

Consider the script test.sh below :

#!/bin/bash
echo "var1 = ${var1}"
echo "var2 = ${var2}"

Then do the following commands :

var1=23
export var2=12
bash test.sh

By default, variables from the main interpreter are not available in scripts, unless you export them.

10/09/2025 | Introduction to Bash Scripting

Differences between Sourcing and Executing a script

  • source a script = execution in the current shell
  • execute a script = execution in a new shell (in a subshell of the current shell)

Source a script using source or .

source test.sh
. test.sh

official one is . Bash defined source as an alias to the .

10/09/2025 | Introduction to Bash Scripting

Example

#!/bin/bash
country="Belgium"
greeting() {
    echo "You're in $1"
}
greeting $country
country="France"
./test.sh or source test.sh
echo $country
greeting $country
10/09/2025 | Introduction to Bash Scripting
> country="France"
> ./test.sh
You're in Belgium
> echo $country
France
> greeting $country
greeting: command not found
> country="France"
> source test.sh
You're in Belgium
> echo $country
Belgium
> greeting $country
You're in Belgium
10/09/2025 | Introduction to Bash Scripting

Debug

Tips and techniques for debugging and troubleshooting Bash scripts

use set -x

enables debugging mode : print each command that it executes to the terminal, preceded by a +

check the exit code

#!/bin/bash
if [ $? -ne 0 ]; then
    echo "Error occurred"
fi
10/09/2025 | Introduction to Bash Scripting

use echo

Classical but useful technique : insert echo throughout your code to check variable content

#!/bin/bash
echo "Value of variable x is: $x"

use set -e

this option will cause Bash to exit with an error if any command in the script fails

10/09/2025 | Introduction to Bash Scripting

Thank you for your attention

10/09/2025 | Introduction to Bash Scripting

Running parallel processes in subshells

Processes may execute in parallel within different subshells

permits breaking a complex task into subcomponents processed concurrently

Exemple : job.sh

#!/bin/bash
job() {
  i=0
  while [ $i -lt 10 ]; do
    echo "${i}: job $job_id"
    (( i++ ))
    sleep 0.2
  done
}

sequential processing (manager_seq.sh) or parallel processing (manager_par.sh)

10/09/2025 | Introduction to Bash Scripting
#!/bin/bash
# manager_seq.sh
source job.sh
echo "start"
for job_id in {1..2}; do job ; done
echo "done"
#!/bin/bash
# manager_par.sh
source job.sh
echo "start"
for job_id in {1..2}; do job & done
wait # Don't execute the next command until subshells finish.
echo "done"
time ./manager_seq.sh
time ./manager_par.sh
10/09/2025 | Introduction to Bash Scripting

Durée max: 10-15 min

## String manipulation Consider `string=abcABC123ABCabc` * string length : `${#string}` is `15` * substring extraction : * `${string:7}` is `23ABCabc` * `${string:7:3}` is `23A` * `${string:(-4)}` or `${string: -4}` is `Cabc` ---

JDF: a mon avis pas utile --- - `let` make a variable equal to an expression ```bash #!/bin/bash let a=5+4 echo $a # 9 let a++ let "a = 4 * $a" ``` - `expr` : print out the result of the expression ```bash #!/bin/bash expr 5 + 4 # 9 expr 5+4 # boom expr "5 + 4" # boom a=$( expr 10 - 3 ) ``` - double parentheses : return the result of the expression > it is the **preferred method**

--- ### Case Statements ```bash #!/bin/bash space_free=$( /usr/bin/df -h | awk '{ print $5 }' | sort -n | tail -n 1 | sed 's/%//' ) case $space_free in [1-5]*) echo "Plenty of disk space available" ;; [6-7]*) echo "There could be a problem in the near future" ;; 8*) echo "Maybe we should look at clearing out old files" ;; 9*) echo "We could have a serious problem on our hands soon" ;; *) echo "Something is not quite right here" ;; esac ```

--- ### Associative arrays = Dictionaries (Bash 4.0 or higher) By default, a bash array is an indexed array : need to use the `declare` command ```bash declare -A acronyms # -A for associative array acronyms[ACK]=Acknowledgement acronyms[EOF]="End of Frame" echo ${acronyms[ACK]} if [ ${acronyms[EOF]+_} ]; then echo "Found"; else echo "Not found"; fi ``` > the variable expansion `${MYVAR+ABC}` expands to `ABC` is `MYVAR` is set and to nothing otherwise ```bash declare -A countries=( [ALB]=Albania [BHR]=Bahrain [CMR]=Cameroon [DNK]=Denmark [EGY]=Egypt ) echo ${countries[@]} echo ${!countries[@]} countries+=( [FJI]=Fiji ) echo ${countries[@]} unset countries[BHR] echo ${countries[@]} ``` <!-- ### About `declare` ```bash declare --help ``` Examples : - declare a variable without a value - force a variable to be an integer only - declare a variable as a **parameter** (read-only) - force a character(s) variable to store all uppercase or lowercase letters - display the attributes and values of all variables - etc.

![bg right 85%](./assets/select_example.png) --- ```bash #!/bin/bash # How to iterate over keys or values of an Array declare -A fruits fruits[south]="Banana" fruits[north]="Orange" fruits[west]="Passion Fruit" fruits[east]="Pineapple" for key in "${!fruits[@]}" do echo "Key is '$key' => Value is '${fruits[$key]}'" done ```

### Flags ```bash #!/bin/bash while getopts u:a:f: flag do case "${flag}" in u) username=${OPTARG};; a) age=${OPTARG};; f) fullname=${OPTARG};; esac done echo "Username: $username"; echo "Age: $age"; echo "Full Name: $fullname"; ``` ```bash bash test_arg.sh -f 'John Smith' -a 25 -u john ``` ---

Jerome: refaire ces 3 slides

`while IFS= read -r line` The internal field separator (`IFS`) is set to the empty string to preserve whitespace issues The `-r` option is used not to allow backslashes to escape any characters

```bash _files="$@" [[ "$_files" == "" ]] && { echo "Usage: $0 file1.png file2.png"; exit 1; } ```

Attention utilisatin du [[ ]] pas encore expliqué ?

## Return Values Bash functions don't allow you to return a value when called After completion, the return value is the **status** of the last statement (so 0-255) It can also be specified manually by using `return` : ```bash my_function () { echo "some result" return 55 } my_function echo $? ``` ---

--- ### Syntax A command list embedded **between parentheses** runs as a subshell : ```bash #!/bin/bash ( command1 ; command2 ; command3 ) ``` Or : ```bash #!/bin/bash bash -c "command1; command2; command3" ``` > Reminder : variables in a subshell are **not** visible outside the block of code in the subshell