This is a full beginner's course on shell scripting that will take you from the very basics to writing simple scripts and connecting them in Unix-based systems.
- Introduction to the Shell
- Writing Your First Shell Script
- Variables in Shell
- Conditionals and Control Flow
- Loops
- Functions in Shell Scripts
- Input and Output
- Working with Files
- Working with Strings and Numbers
- Advanced Scripting Techniques
- Additional Topics
A shell is a command-line interpreter that allows users to interact with the operating system. It processes user commands and requests the operating system to perform the specified tasks.
Shell | Description |
---|---|
Bash | Bourne Again Shell - Default on many Unix-based systems. |
Zsh | Z Shell - Offers improvements over Bash. |
Fish | Friendly Interactive Shell - User-friendly features. |
Ksh | Korn Shell - Combines features of Bourne and C Shells. |
To check which shell you're using, type:
echo $SHELL
Basic navigation commands:
Command | Description |
---|---|
pwd |
Show current directory. |
cd |
Change directory. |
ls |
List files in a directory. |
#!/bin/bash
# Display current directory
echo "Current Directory: $(pwd)"
# List files
echo "Files in Directory:"
ls
A shell script is a text file containing a sequence of commands for the shell to execute.
#!/bin/bash
echo "Hello, World!"
Steps:
-
Create the script file:
nano hello_world.sh
-
Add the content above and save the file.
-
Make the script executable:
chmod +x hello_world.sh
-
Run the script:
./hello_world.sh
Use #
for comments.
#!/bin/bash
# This script prints Hello, World!
echo "Hello, World!"
#!/bin/bash
NAME="Adamo"
AGE=21
echo "My name is $NAME and I am $AGE years old."
#!/bin/bash
NAME="Adamo"
AGE=21
echo "My name is $NAME and I am $AGE years old."
Variable | Description |
---|---|
$0 |
The name of the script. |
$1 - $9 |
The first to ninth arguments to the script. |
$@ |
All the arguments. |
$# |
Number of arguments. |
$$ |
Process ID of the script. |
$? |
Exit status of the last command. |
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "All arguments: $@"
echo "Number of arguments: $#"
Quote Type | Description |
---|---|
' ' (Single Quotes) |
Prevents variable expansion. |
" " (Double Quotes) |
Allows variable expansion. |
` ` (Backticks) |
Executes commands inside. |
#!/bin/bash
NAME="Adamo"
echo 'Hello $NAME' # Outputs: Hello $NAME
echo "Hello $NAME" # Outputs: Hello Adamo
DATE=`date`
echo "Today is $DATE"
if [ condition ]; then
# commands
fi
#!/bin/bash
NAME="Adamo"
if [ "$NAME" == "Adamo" ]; then
echo "Hello Adamo!"
fi
if [ condition ]; then
# commands
elif [ condition ]; then
# commands
else
# commands
fi
#!/bin/bash
NAME=$1 # Get the first argument
if [ "$NAME" == "Adamo" ]; then
echo "Hello Adamo!"
elif [ "$NAME" == "Sofia" ]; then
echo "Hello Sofia!"
else
echo "Hello Stranger!"
fi
test
, [ ]
, and [[ ]]
are used to evaluate conditions in shell scripts, but they have some differences:
test
is a command that evaluates conditions. It can also be written using the[ ]
shorthand.- Both
test
and[ ]
support basic comparisons and are POSIX-compliant, meaning they work on all Unix-like systems. - Example:
if test "$NUM1" -eq "$
NUM2"; then
echo "Numbers are equal"
fi
# Equivalent to
if [ "$NUM1" -eq "$NUM2" ]; then
echo "Numbers are equal"
fi
[[ ]]
is an enhanced version of[ ]
with additional functionality, such as pattern matching and better support for complex expressions.- It is bash-specific (not POSIX), meaning it won't work on all shells, but it is more powerful.
- Example:
if [[ "$STRING" =~ ^[a-z]+$ ]]; then
echo "String is lowercase"
fi
[[ ]]
allows for more complex conditionals and doesn’t require escaping certain characters, while[ ]
is more limited and requires careful quoting and escaping.[[ ]]
is considered safer when using string comparisons because it prevents many unexpected issues caused by shell expansions.
#!/bin/bash
NUM1=10
NUM2=20
if [ "$NUM1" -lt "$NUM2" ]; then
echo "$NUM1 is less than $NUM2"
fi
STR="hello"
if [[ "$STR" =~ ^[a-z]+$ ]]; then
echo "$STR contains only lowercase letters"
fi
Operator | Description | For Numbers | For Strings |
---|---|---|---|
-eq |
Equal | ✔️ | |
-ne |
Not equal | ✔️ | |
-gt |
Greater than | ✔️ | |
-lt |
Less than | ✔️ | |
-ge |
Greater or equal | ✔️ | |
-le |
Less or equal | ✔️ | |
= |
Equal | ✔️ | |
!= |
Not equal | ✔️ | |
-z |
String is empty | ✔️ | |
-n |
String is not empty | ✔️ |
#!/bin/bash
NUM1=10
NUM2=20
if [ $NUM1 -lt $NUM2 ]; then
echo "$NUM1 is less than $NUM2"
fi
STR="Hello"
if [ -n "$STR" ]; then
echo "String is not empty"
fi
case variable in
pattern1)
# commands
;;
pattern2)
# commands
;;
*)
# default commands
;;
esac
#!/bin/bash
COLOR=$1 # Get the color from the first argument
case $COLOR in
"red")
echo "The color is red."
;;
"blue")
echo "The color is blue."
;;
"green")
echo "The color is green."
;;
*)
echo "Unknown color."
;;
esac
for variable in list; do
# commands
done
#!/bin/bash
for i in {1..5}; do
echo "Number: $i"
done
while [ condition ]; do
# commands
done
#!/bin/bash
i=1
while [ $i -le 5 ]; do
echo "Number: $i"
i=$((i + 1))
done
until [ condition ]; do
# commands
done
#!/bin/bash
i=1
until [ $i -gt 5 ]; do
echo "Number: $i"
i=$((i + 1))
done
Use break
and continue
to control loop execution.
#!/bin/bash
for i in {1..10}; do
if [ $i -eq 5 ]; then
echo "Skipping number 5"
continue
fi
if [ $i -gt 8 ]; then
echo "Breaking at number $i"
break
fi
echo "Number: $i"
done
function_name() {
# commands
}
#!/bin/bash
greet() {
echo "Hello, $1!"
}
greet "Adamo"
#!/bin/bash
greet() {
echo "Hello, $1!"
}
greet "Adamo"
greet "Sofia"
#!/bin/bash
echo "This is a message."
#!/bin/bash
read -p "Enter your name: " NAME
echo "Hello, $NAME!"
Symbol | Description |
---|---|
> |
Redirect output to a file (overwrite). |
>> |
Redirect output to a file (append). |
< |
Redirect input from a file. |
2> |
Redirect stderr to a file. |
&> |
Redirect both stdout and stderr to a file. |
#!/bin/bash
ls -l > files_list.txt
echo "Directory listing saved to files_list.txt"
#!/bin/bash
# Create a file
touch myfile.txt
# Move the file
mv myfile.txt mynewfile.txt
# Copy the file
cp mynewfile.txt copy_of_mynewfile.txt
# Delete the files
rm mynewfile.txt copy_of_mynewfile.txt
Change file permissions using chmod
.
#!/bin/bash
# Give read, write, and execute permissions to the user
chmod u+rwx script.sh
# Remove write permission from group and others
chmod go-w script.sh
*
matches any number of characters.?
matches a single character.
#!/bin/bash
# List all .txt files
ls *.txt
# List files starting with 'file' and any single character
ls file?.txt
Test Expression | Description |
---|---|
-e FILE |
File exists. |
-f FILE |
File exists and is a regular file. |
-d DIRECTORY |
Directory exists. |
-s FILE |
File is not empty. |
-r FILE |
File is readable. |
-w FILE |
File is writable. |
-x FILE |
File is executable. |
#!/bin/bash
FILE="testfile.txt"
if [ -e "$FILE" ]; then
echo "$FILE exists."
else
echo "$FILE does not exist."
fi
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -s "$FILE" ]; then
echo "$FILE is not empty."
fi
#!/bin/bash
STR="Hello World"
SUBSTR=${STR:0:5}
echo "Substring: $SUBSTR" # Outputs: Hello
#!/bin/bash
STR="Hello"
LENGTH=${#STR}
echo "Length: $LENGTH" # Outputs: 5
Use $(( expression ))
for arithmetic operations.
#!/bin/bash
NUM1=10
NUM2=5
SUM=$((NUM1 + NUM2))
DIFF=$((NUM1 - NUM2))
PROD=$((NUM1 * NUM2))
QUOT=$((NUM1 / NUM2))
MOD=$((NUM1 % NUM2))
echo "Sum: $SUM"
echo "Difference: $DIFF"
echo "Product: $PROD"
echo "Quotient: $QUOT"
echo "Remainder: $MOD"
#!/bin/bash
# Replace 'old' with 'new' in file.txt
sed 's/old/new/g' file.txt > newfile.txt
#!/bin/bash
# Print the first column of a file
awk '{print $1}' file.txt
#!/bin/bash
read -p "Enter your email: " EMAIL
if [[ $EMAIL =~ ^[a-zA-Z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$ ]]; then
echo "Valid email."
else
echo "Invalid email."
fi
To schedule tasks, edit the crontab file.
-
Open the crontab editor:
crontab -e
-
Add the following line:
0 2 * * * /path/to/script.sh
Use -x
option to trace script execution.
bash -x script.sh
Every command returns an exit status (0 for success, non-zero for failure).
#!/bin/bash
mkdir new_directory
if [ $? -eq 0 ]; then
echo "Directory created successfully."
else
echo "Failed to create directory."
fi
Alternatively, use set -e
to exit the script if any command fails.
Use getopts
for parsing options.
#!/bin/bash
while getopts ":u:p:" opt; do
case $opt in
u) USERNAME="$OPTARG"
;;
p) PASSWORD="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done
echo "Username: $USERNAME"
echo "Password: $PASSWORD"
Run the script:
./script.sh -u "adamo" -p "password123"
Environment variables are available to all child processes.
#!/bin/bash
echo "Home Directory: $HOME"
echo "Current User: $USER"
echo "Path Variable: $PATH"
Handle signals using trap
.
#!/bin/bash
trap "echo 'Script interrupted! Exiting...'; exit 1" SIGINT
echo "Press Ctrl+C to interrupt."
while true; do
sleep 1
done
This guide covers the fundamentals of shell scripting, providing examples for each topic to help you practice and understand how shell scripts work. By working through these examples, you'll gain a solid foundation in shell scripting and be prepared to tackle more complex scripting tasks.
Happy Scripting!
- Bash Reference Manual: https://www.gnu.org/software/bash/manual/bash.html
- Advanced Bash-Scripting Guide: http://tldp.org/LDP/abs/html/