Skip to main content

Writing Bash Scripts

1. Creating Script File

Example: Basic Bash Script File

#!/bin/bash

echo "Hello, World!"
Step Description Action
1 Choose a location to save the script Create a new file in your preferred directory (e.g., ~/scripts/)
2 Add the shebang line Start with #!/bin/bash to indicate the interpreter
3 Write the script content Add commands and logic as needed
4 Save the file Ensure it has a meaningful name (e.g., hello.sh)
5 Make it executable Run chmod +x hello.sh
Note: The shebang (#!) tells the system which interpreter to use when running the script.

2. Making Scripts Executable

Step Description Action
1 Write the script content Create a file with your bash commands, e.g., script.sh
2 Add shebang Start with #!/bin/bash as the first line
3 Set execute permissions Run chmod +x script.sh
4 Run the script Execute with ./script.sh

Example: Basic Executable Script

#!/bin/bash
echo "Hello, World!"
Note: The shebang line tells the system which interpreter to use to run the script.

3. Using Variables

Variable Type Description Example
VAR_NAME Local Standard variable declaration name="John"
export VAR_NAME Global Makes variable available to child processes export PATH="/usr/bin"
$VAR_NAME Reference Accesses variable value echo "Hello $name"
${VAR_NAME} Brace Expansion Used to avoid ambiguity in variable names echo "User: ${USER}"
VAR_NAME=value Assignment Assigns value to variable count=5

Example: Variable Assignment and Usage

name="Alice"
age=30
echo "Hello, $name! You are $age years old."

Example: Exporting Variables

export HOME="/home/user"
export PATH="$PATH:/usr/local/bin"
Note: Variables in Bash are case-sensitive and must not contain spaces around the equals sign.
Warning: Using VAR_NAME=value without export limits the variable scope to the current shell session.

4. Reading User Input

Command Description Example
read Reads a line of input from the user
read name
echo "Hello, $name"
read -p Displays a prompt before reading input
read -p "Enter your name: " name
echo "Hello, $name"
read -s Suppresses echoing input (useful for passwords)
read -s -p "Enter password: " password
echo
echo "Password entered"
read -t Times out after specified seconds
read -t 5 -p "Enter input within 5 seconds: " input
if [ -z "$input" ]; then
  echo "Timeout"
else
  echo "You entered: $input"
fi
Note: The read command reads from standard input (stdin) by default.
Warning: Be cautious with -s option as it may not work consistently across all terminal environments.

5. Using Command-Line Arguments

Variable Description Example
$0 Name of the script ./my_script.sh
$1 First argument ./script.sh arg1
$2 Second argument ./script.sh arg1 arg2
$_ Last argument passed to the script ./script.sh arg1 arg2
$# Number of arguments ./script.sh arg1 arg2 → 2
$* All arguments as a single string ./script.sh "arg1 arg2"
$@ All arguments as separate strings ./script.sh arg1 arg2
$? Exit status of the last command echo $?

Example: Accessing Arguments

#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "Total arguments: $#"

Example: Looping Through Arguments

#!/bin/bash
for arg in "$@"; do
  echo "Argument: $arg"
done

Example: Handling Optional Arguments

#!/bin/bash
if [ $# -eq 0 ]; then
  echo "No arguments provided"
else
  echo "First argument is: $1"
fi

6. Implementing Conditional Statements

Statement Syntax Description
if
if condition; then
    # commands
fi
Basic conditional execution
if-else
if condition; then
    # commands
else
    # commands
fi
Execute commands based on condition
if-elif-else
if condition1; then
    # commands
elif condition2; then
    # commands
else
    # commands
fi
Multiple condition checks

Example: Basic if-else

#!/bin/bash
if [ "$USER" = "root" ]; then
    echo "You are root"
else
    echo "You are not root"
fi

Example: Nested if-else

#!/bin/bash
if [ "$USER" = "root" ]; then
    if [ -d "/etc" ]; then
        echo "Root user with access to /etc"
    fi
else
    echo "Not root user"
fi
Note: Use [ ] for tests, not [[ ]] unless you need extended features like pattern matching or string operators.

Example: Using test command

#!/bin/bash
if test -f "file.txt"; then
    echo "file.txt exists"
else
    echo "file.txt does not exist"
fi

7. Using Test Conditions

Condition Description Examples
[ string ] True if string is non-empty [ "hello" ]
[ -z string ] True if string is empty [ -z "$var" ]
[ -n string ] True if string is non-empty [ -n "$var" ]
[ string1 = string2 ] True if strings are equal [ "$a" = "$b" ]
[ string1 != string2 ] True if strings are not equal [ "$a" != "$b" ]
[ integer1 -eq integer2 ] True if integers are equal [ "$a" -eq 5 ]
[ integer1 -ne integer2 ] True if integers are not equal [ "$a" -ne 5 ]
[ integer1 -lt integer2 ] True if integer1 is less than integer2 [ "$a" -lt 10 ]
[ integer1 -le integer2 ] True if integer1 is less than or equal to integer2 [ "$a" -le 10 ]
[ integer1 -gt integer2 ] True if integer1 is greater than integer2 [ "$a" -gt 5 ]
[ integer1 -ge integer2 ] True if integer1 is greater than or equal to integer2 [ "$a" -ge 5 ]
[ -f filename ] True if file exists and is a regular file [ -f "/etc/passwd" ]
[ -d directory ] True if directory exists [ -d "/tmp" ]
[ -r filename ] True if file is readable [ -r "/etc/passwd" ]
[ -w filename ] True if file is writable [ -w "/tmp/script.sh" ]
[ -x filename ] True if file is executable [ -x "/bin/ls" ]
[ -e filename ] True if file exists (regardless of type) [ -e "/etc/passwd" ]
[ -s filename ] True if file exists and has size greater than zero [ -s "/tmp/file" ]
[ -L filename ] True if file is a symbolic link [ -L "/tmp/link" ]
[ -O filename ] True if file is owned by the effective user ID [ -O "/tmp/file" ]
[ -G filename ] True if file is owned by the effective group ID [ -G "/tmp/file" ]
[ -N filename ] True if file has been modified since last read [ -N "/tmp/file" ]
[ -B filename ] True if file is a block device [ -B "/dev/sda" ]
[ -c filename ] True if file is a character device [ -c "/dev/tty" ]
[ -p filename ] True if file is a named pipe (FIFO) [ -p "/tmp/fifo" ]
[ -S filename ] True if file is a socket [ -S "/tmp/socket" ]
[ -u filename ] True if file has the set-user-ID bit set [ -u "/usr/bin/passwd" ]
[ -g filename ] True if file has the set-group-ID bit set [ -g "/usr/bin/sudo" ]
[ -k filename ] True if file has the sticky bit set [ -k "/tmp/dir" ]
[ -t fd ] True if file descriptor is a terminal [ -t 0 ]
[ -o option ] True if shell option is set [ -o errexit ]
[ -a file ] True if file exists (alias for -e) [ -a "/etc/passwd" ]
[ -b file ] True if file is a block special device [ -b "/dev/sda" ]
[ -h file ] True if file is a symbolic link (alias for -L) [ -h "/tmp/link" ]
[ -w file ] True if file is writable (alias for -w) [ -w "/tmp/file" ]
[ -x file ] True if file is executable (alias for -x) [ -x "/bin/ls" ]
[ -z string ] True if string is empty (alias for -z) [ -z "$var" ]
[ -n string ] True if string is non-empty (alias for -n) [ -n "$var" ]

Example: Using test conditions in an if statement

#!/bin/bash

if [ -f "/etc/passwd" ]; then
    echo "The file /etc/passwd exists."
else
    echo "The file /etc/passwd does not exist."
fi

if [ "$USER" = "root" ]; then
    echo "You are root."
else
    echo "You are not root."
fi

Example: Combining test conditions with logical operators

#!/bin/bash

if [ "$USER" = "root" ] && [ -d "/root" ]; then
    echo "You are root and /root directory exists."
fi

if [ "$USER" = "root" ] || [ "$USER" = "admin" ]; then
    echo "You are either root or admin."
fi
Note: The [ command is a synonym for the test command. Both forms are equivalent.
Warning: Always quote variables in test conditions to prevent word splitting and pathname expansion. For example, use [ "$var" = "value" ] instead of [ $var = value ].

8. Implementing Loops

Loop Type Description Example
for Iterates over a list of items or a range of numbers
for i in 1 2 3; do
  echo "Number: $i"
done
while Continues executing as long as a condition is true
count=0
while [ $count -lt 3 ]; do
  echo "Count: $count"
  count=$((count + 1))
done
until Executes until a condition becomes true
count=0
until [ $count -ge 3 ]; do
  echo "Count: $count"
  count=$((count + 1))
done

Example: Looping Through Files

for file in /home/user/*.txt; do
  echo "Processing $file"
done

Example: Nested Loops

for i in {1..3}; do
  for j in {1..2}; do
    echo "i=$i, j=$j"
  done
done
Note: The for loop in Bash can iterate over arrays, command output, or a list of items. The while loop continues as long as the condition evaluates to true.
Warning: Infinite loops can occur if the loop condition never becomes false. Always ensure that the loop variable is updated inside the loop body.

9. Using Case Statements

Structure Description Example
case $variable in Starts the case statement with a variable to test case $1 in
pattern) Defines a pattern to match against the variable
start)
    echo "Starting service"
    ;;
*) Default case if no patterns match
*) 
    echo "Unknown option"
    ;;

Example: Basic Case Statement

#!/bin/bash
case $1 in
    start)
        echo "Starting service"
        ;;
    stop)
        echo "Stopping service"
        ;;
    restart)
        echo "Restarting service"
        ;;
    *)
        echo "Unknown option: $1"
        exit 1
        ;;
esac
Note: The ;; is required to end each case block, and the *) pattern matches any value not matched by previous patterns.

10. Defining Functions

Aspect Description Notes
Function Definition Syntax function_name() { ... } Alternative syntax: function function_name { ... }
Function with Parameters greet() { echo "Hello, $1"; } Use $1, $2, etc. for arguments
Return Values return 0 Only exit status (0-255) can be returned. Use echo for output.
Local Variables local var_name=value Prevents variable leakage to global scope

Example: Basic Function Definition

greet() {
    echo "Hello, World!"
}

greet

Example: Function with Parameters

calculate() {
    local result=$(( $1 + $2 ))
    echo "Sum is: $result"
}

calculate 5 3

Example: Function with Local Variables

process_data() {
    local temp_file="/tmp/data_$"
    echo "Processing..." > "$temp_file"
    cat "$temp_file"
    rm "$temp_file"
}

process_data
Note: Functions must be defined before they are called. Bash does not support forward declarations.

11. Handling Exit Codes

Exit Code Description Usage
0 Successful execution Default return value for successful commands
1 General error Most common non-zero exit code for failures
2 Usage error Incorrect usage of a command (e.g., missing arguments)
126 Command cannot execute Permission denied or command not found
127 Command not found Shell cannot find the command in PATH
128 Invalid argument to exit Exit with invalid argument (e.g., exit 256)
128+n Terminated by signal n Exit due to a signal (e.g., 128+9 = killed by SIGKILL)

Example: Using exit codes in a script

#!/bin/bash

if ls /nonexistent > /dev/null 2&&1; then
    echo "Directory exists"
    exit 0
else
    echo "Directory does not exist"
    exit 1
fi

Example: Checking exit code of a command

ls /etc
if [ $? -eq 0 ]; then
    echo "Command succeeded"
else
    echo "Command failed with exit code: $?"
fi
Note: The special variable $? holds the exit status of the last executed command.
Warning: Exit codes are integers from 0 to 255. Values outside this range wrap around (e.g., exit 256 is equivalent to exit 0).

12. Implementing Error Handling

Handler Description Usage
set -e Exit immediately if a command exits with a non-zero status set -e
set -u Exit on use of undefined variables set -u
set -o pipefail Make pipelines fail if any component fails set -o pipefail

Example: Using set options

set -e
set -u
set -o pipefail

'command that might fail' | 'another command that might fail'
Note: These options should be set at the beginning of your script to ensure consistent error behavior.
Method Description Example
trap Catch signals and clean up before exit trap 'rm -f /tmp/myfile' EXIT
check exit status Use $? to check command success if [ $? -ne 0 ]; then echo "Failed"; exit 1; fi
function error handling Wrap commands in functions with error checks myfunc() { command || return 1; }

Example: Trap usage

temp_file="/tmp/script_temp"

trap 'rm -f $temp_file' EXIT

echo "Processing..." > $temp_file

Example: Function error handling

process_data() {
    if ! command "some_operation"; then
        echo "Operation failed"
        return 1
    fi
}

if process_data; then
    echo "Success"
else
    echo "Failed to process data"
    exit 1
fi

Was this article helpful?