SHELL SCRIPTING PART 2
BY NAVEED
1. EXIT STATUS: THE CONCEPT
In Linux, every single command returns a hidden number called the Exit Status (or Return Code) when it finishes.
This integer ranges from 0 to 255.
0 represents Success.
Non-Zero 1255 represents various types of Failure.
The shell stores the exit status of the last executed command in the special variable $?.
Checking $? immediately is crucial for error handling in automation.
02 / 25 By
Naveed
EXIT STATUS: SUCCESS EXAMPLE
Let's see what happens when a command runs
successfully. [Link]
Here, ls /tmp is a valid command (assuming /tmp
exists). # Run a valid command
ls /tmp > /dev/null
We redirect output to /dev/null to keep the terminal
clean. # Check the exit status
echo "Exit Status: $?"
We immediately echo $? to see the result.
The result 0 confirms the command worked
perfectly.
03 / 25 By
Naveed
EXIT STATUS: FAILURE EXAMPLE
[Link]
Now, let's force an error to see a non-zero code.
The false command in Linux does nothing but return
false # Built-in command to fail
an exit code of 1. echo "False Status: $?"
Similarly, trying to ls a file that doesn't exist returns 2
ls /non/existent/file
No such file or directory). echo "LS Error Status: $?"
Scripts use these codes to decide whether to stop or
retry.
04 / 25 By
Naveed
CUSTOM EXIT CODES
You can manually set the exit code of your own script
custom_exit.sh
using the exit command.
If your script encounters a critical error (like missing if [ -z "$1" ]; then echo "Error: No name
arguments), use exit 1 (or any number > 0. provided!" exit 2 # Custom error code fi
echo "Hello $1" exit 0 # Success
This tells the parent process (or user) that your script
failed.
Always end a successful script with exit 0 (though
it's often implied).
05 / 25 By
Naveed
2. VARIABLES: GLOBAL SCOPE
[Link]
By default, all variables in Bash are Global. MY_VAR="Original"
If you define a variable anywhere in the script (even
change_it() {
inside a function), it is visible and modifiable by the MY_VAR="Changed!"
}
entire script.
This can cause bugs if different functions try to use
change_it
the same variable name (like i or count). echo "$MY_VAR"
06 / By
25 Naveed
VARIABLES: LOCAL SCOPE
[Link]
To prevent side effects, use the local keyword inside
my_func() {
functions.
local temp="Invisible outside"
local var=value creates a variable that exists only
echo "Inside: $temp"
within that function. }
It shadows any global variable with the same name.
my_func
Once the function finishes, the local variable is echo "Outside: $temp"
destroyed, and the global one remains untouched.
07 / By
25 Naveed
VARIABLE SCOPE COLLISION
[Link]
This example proves why local is a best practice. NAME="Naveed"
We have a global NAME "Naveed". test_scope() {
local NAME="Guest"
The function defines a local NAME "Guest". echo "In Func: $NAME"
}
Inside the function, it sees "Guest".
After the function, the global "Naveed" is perfectly test_scope
echo "Global: $NAME"
preserved.
08 / 25 By
Naveed
3. FUNCTIONS: DEFINITION
Functions allow you to group commands into a reusable [Link]
block.
Syntax: name() { commands... } # Definition
welcome() {
You can also use the keyword function name { ... }. echo "Welcome to DevOps!"
echo "Let's script."
}
Important: You do NOT put parentheses () when
# Invocation (No parentheses!)
calling the function, only when defining it.
welcome
Define functions at the top of your script before you
call them.
09 / 25 By
Naveed
FUNCTIONS: ARGUMENTS
[Link]
Functions have their own set of Positional Parameters.
Inside a function, $1 is the first argument passed to greet() {
# $1 is the first argument
the function, not the script. echo "Hello, $1!"
$2 is the second argument, and so on. }
$# is the number of arguments passed to the # Pass "Naveed" as argument
greet "Naveed"
function.
greet "World"
This allows functions to be dynamic and reusable.
10 / By
25 Naveed
FUNCTIONS: RETURN STATUS
return_code.sh
The return keyword in a function sets the Exit Status is_valid_user() {
0255. if [ "$1" == "Naveed" ]; then
It does NOT return a value/string like Python or return 0 # Success
else
JavaScript. return 1 # Failure
It is used to indicate Success 0) or Failure fi
}
(non-zero).
You capture it with $? immediately after the function is_valid_user "Bob"
echo "Result: $?"
call.
11 / By
25 Naveed
FUNCTIONS: RETURN OUTPUT
return_val.sh
To get data (strings/numbers) out of a function, you
get_time() {
must echo it to standard output. date +%H:%M
The caller captures this output using Command }
Substitution: $(function_name).
# Capture output into variable
This assigns everything the function printed to the NOW=$(get_time)
variable.
echo "Current time is $NOW"
This is the standard way to "return" values in Bash.
12 / By
25 Naveed
4. CASE STATEMENT
[Link]
The case statement is a cleaner alternative to multiple case "$VAR" in "pattern1") # Commands for
pattern1 ;; "pattern2") # Commands for pattern2
if-elif-else blocks.
It matches a variable against several patterns. ;; *) # Default commands ;; esac
Syntax ends with esac (case spelled backwards).
Each pattern block ends with double semicolons ;;.
*) acts as the "default" or "else" catch-all block.
13 / By
25 Naveed
CASE STATEMENT EXAMPLE
Here is a real-world example: Handling command-line [Link]
arguments to start or stop a service.
case "$1" in
We check the value of $1.
start) echo "Starting app..." ;;
We handle "start", "stop", and "restart". stop) echo "Stopping app..." ;;
restart) echo "Restarting..." ;;
Any other input triggers the Usage message. *) echo "Usage: $0 {start|stop}" ;;
esac
This pattern is used in almost every system init
script.
14 / By
25 Naveed
5. LOOPS: FOR LOOP (LIST)
The classic for loop iterates over a list of items. for_list.sh
Useful for processing a list of strings, filenames, or
# Iterate over explicit strings
server names. for fruit in Apple Banana Cherry; do
The variable item takes the value of each element in echo "I like $fruit"
done
the list one by one.
You can perform the same operation on every item.
15 / By
25 Naveed
LOOPS: FOR LOOP (C-STYLE)
Bash supports C-style loops for numeric iteration. c_style.sh
Syntax: (( initial; condition; increment )).
# Count from 1 to 3
Best used when you need a specific number of for (( i=1; i<=3; i++ )); do
echo "Counter: $i"
iterations or an index counter.
done
Note the double parentheses (( )) which are specific
to Bash arithmetic.
16 / By
25 Naveed
LOOPS: WHILE LOOP
The while loop continues running as long as the [Link]
condition is True.
Useful for reading files line-by-line. count=1
while [ $count -le 3 ]; do
Useful for creating "daemon" scripts that run forever echo "Count: $count"
count=$((count + 1))
(while true).
done
Remember to update the condition variable (like
count) to avoid infinite loops!
17 / By
25 Naveed
LOOPS: UNTIL LOOP
[Link]
The until loop is the opposite of while.
It runs until the condition becomes True. count=1
# Run UNTIL count > 3
In other words, it runs as long as the condition is until [ $count -gt 3 ]; do
echo "Wait: $count"
False.
count=$((count + 1))
Great for waiting for a resource to become available done
(like waiting for a server to ping).
18 / By
25 Naveed
LOOPS: SELECT (MENUS)
[Link]
select allows you to create interactive menus easily. PS3="Choose: "
select opt in Run Quit; do
It automatically prints a numbered list of options.
case $opt in
It prompts the user to enter a number. Run) echo "Running..." ;;
Quit) break ;;
Often combined with case to handle the choice. *) echo "Invalid" ;;
esac
Set PS3 to change the prompt string. done
19 / By
25 Naveed
6. DEBUGGING OVERVIEW
Scripts will fail. Debugging is about finding out why and where.
Trace Mode: Prints commands before executing them.
Exit on Error: Stops script immediately if a command fails.
Syntax Check: Checks code without running it.
Linting: Use external tools like shellcheck.
20 / 25 By
Naveed
DEBUGGING: SET MODES
debug_modes.sh
The set command controls shell options. # Enable trace & exit on error
set -x (xtrace): Prints each command to stderr before set -xe
execution. Great for tracing logic.
NAME="Naveed"
set -e (errexit): Aborts script if any command returns echo "Hello $NAME"
a non-zero exit status.
ls /nonexistent # Script dies here
set -u (nounset): Treats unset variables as an error echo "This won't run"
(prevents "rm -rf /" disasters).
21 / By
25 Naveed
DEBUGGING: TRAP
trap allows you to catch signals and errors to perform [Link]
cleanup.
trap 'echo "Error at line $LINENO!"' ERR
Catch ERR to run code when a command fails.
Catch EXIT to run code when the script finishes echo "Running..."
ls /missing_file
(successfully or not).
echo "Done"
Commonly used to remove temporary files or log
errors.
22 / 25 By
Naveed
7. SED (STREAM EDITOR)
sed is a powerful tool for parsing and transforming text
in a stream (file or pipeline).
Most common use: Find and Replace.
basic_sed.sh
Basic Syntax: s/search_pattern/replacement/flags.
echo "Hello World" | sed 's/World/Naveed/'
By default, it prints the modified text to the screen
Standard Output).
It does not modify the original file unless you use a
specific flag.
23 / 25 By
Naveed
SED: INLINE EDIT & DELETION
advanced_sed.sh
-i: Edit file In-place (save changes to file).
# Replace OLD with NEW in file sed -i
d: Delete matching lines.
's/OLD/NEW/g' [Link]
# Delete lines containing "error" sed -i
Warning: -i is destructive. Test your regex without it
'/error/d' [Link]
first!
24 / 25 By
Naveed
THANKS FOR READING! 🙏
You've leveled up to Intermediate Shell Scripting.
Follow Naveed Ibrahim A for more daily
DevOps bites.
Snail Cooked By Naveed | DevOps & Cloud
Learner