dotlinux guide

Step-by-Step: Writing Your First Linux Command Line Script

In the world of Linux, the command line is a powerful tool, but manually typing repetitive commands can be time-consuming and error-prone. Enter shell scripting—a way to automate tasks by combining Linux commands into reusable scripts. Whether you’re a developer, system administrator, or casual user, learning to write shell scripts will boost your productivity and deepen your understanding of Linux. This guide will walk you through creating your first Linux command line script, from basic syntax to advanced concepts like variables, loops, and conditionals. By the end, you’ll be able to write scripts to automate tasks like file management, log analysis, and system monitoring.

Table of Contents

  1. Prerequisites
  2. Understanding the Linux Shell
  3. Creating Your First Script: “Hello World”
  4. Making Scripts Executable
  5. Variables in Scripts
  6. Reading User Input
  7. Conditional Statements (if-else)
  8. Loops: Automating Repetitive Tasks
  9. Functions: Reusable Code Blocks
  10. Common Practices
  11. Best Practices
  12. Troubleshooting Scripts
  13. Conclusion
  14. References

Prerequisites

Before diving in, ensure you have:

  • A Linux environment (physical machine, VM, or WSL for Windows users).
  • Basic familiarity with Linux commands (e.g., ls, cd, echo, chmod).
  • A text editor (e.g., nano, vim, or VS Code with the Remote - WSL extension).

Understanding the Linux Shell

A shell is a command-line interpreter that acts as an interface between the user and the Linux kernel. When you run commands in the terminal, the shell executes them. Common shells include:

  • Bash (Bourne Again Shell): Default on most Linux distributions (e.g., Ubuntu, Fedora).
  • Zsh: Popular for its customization and plugins.
  • Sh (Bourne Shell): Older, less feature-rich.

We’ll use Bash (the most common) for scripting. To confirm your shell, run:

echo $SHELL  # Output: /bin/bash (if using Bash)

Creating Your First Script: “Hello World”

Let’s start with a classic “Hello World” script to learn the basics.

Step 1: Create the Script File

Open your text editor and create a new file named hello.sh (the .sh extension is conventional for shell scripts):

nano hello.sh

Step 2: Add the Shebang Line

Every Bash script should start with a shebang line (#!), which tells the system which shell to use to execute the script. For Bash, this is:

#!/bin/bash

Step 3: Add Commands

Below the shebang, add a command to print “Hello, World!” using echo:

#!/bin/bash

echo "Hello, World!"

Step 4: Save and Exit

In nano, press Ctrl+O to save, then Ctrl+X to exit.

Making Scripts Executable

To run the script, you need to grant it execute permission. By default, files you create have read/write permissions but not execute.

Step 1: Check Permissions

Verify the current permissions with ls -l:

ls -l hello.sh  # Output: -rw-r--r-- 1 user user 30 Sep 1 12:00 hello.sh

The -rw-r--r-- means:

  • rw-: Owner can read/write.
  • r--: Group can read.
  • r--: Others can read.

Step 2: Add Execute Permission

Use chmod to add execute permission for the owner:

chmod +x hello.sh

Now check permissions again:

ls -l hello.sh  # Output: -rwxr--r-- 1 user user 30 Sep 1 12:00 hello.sh

The x in -rwx indicates execute permission.

Step 3: Run the Script

Execute the script with ./hello.sh (the ./ tells the shell to look in the current directory):

./hello.sh  # Output: Hello, World!

If you omit ./ and run hello.sh directly, the shell will search for it in your $PATH (system directories like /bin), which won’t include your current folder by default.

Variables in Scripts

Variables store data for reuse in scripts. They make scripts dynamic and flexible.

Declaring Variables

To declare a variable, use VAR_NAME=value (no spaces around =):

NAME="Alice"
AGE=30

Accessing Variables

To use a variable, prefix it with $:

echo "Name: $NAME"  # Output: Name: Alice
echo "Age: $AGE"    # Output: Age: 30

Example: Variable Script

Update hello.sh to use a variable:

#!/bin/bash

NAME="Alice"
echo "Hello, $NAME!"  # Output: Hello, Alice!

Command Substitution

Use $(command) or `command` to store the output of a command in a variable (command substitution):

#!/bin/bash

# Get current date
DATE=$(date +"%Y-%m-%d")
echo "Today is $DATE"  # Output: Today is 2024-09-01

Reading User Input

Use the read command to get input from the user during script execution.

Basic Input

Prompt the user for their name and greet them:

#!/bin/bash

# Prompt user for input ( -p = prompt message )
read -p "Enter your name: " NAME

# Greet the user
echo "Hello, $NAME!"

Run it:

./hello.sh
# Output: Enter your name: Bob
#         Hello, Bob!

Silent Input (e.g., Passwords)

Use -s to hide input (useful for passwords):

#!/bin/bash

read -sp "Enter your password: " PASSWORD  # -s = silent
echo  # Add a newline after input
echo "Password received (but we won't display it!)"

Conditional Statements (if-else)

Conditionals let your script make decisions. Use if-else to execute code based on whether a condition is true or false.

Syntax

if [ condition ]; then
  # Code to run if condition is true
elif [ another_condition ]; then
  # Code to run if first condition is false, but this is true
else
  # Code to run if all conditions are false
fi  # Ends the if statement

Common Conditions

ConditionMeaning
[ $a -eq $b ]$a equals $b (numbers)
[ $a -ne $b ]$a not equal to $b
[ $a -lt $b ]$a less than $b
[ $a -gt $b ]$a greater than $b
[ -f "file" ]file exists and is a regular file
[ -d "dir" ]dir exists and is a directory
[ "$str1" = "$str2" ]Strings str1 and str2 are equal

Example: Check if a File Exists

#!/bin/bash

read -p "Enter a filename: " FILE

if [ -f "$FILE" ]; then
  echo "$FILE exists and is a regular file."
elif [ -d "$FILE" ]; then
  echo "$FILE exists but is a directory, not a file."
else
  echo "$FILE does not exist."
fi

Loops: Automating Repetitive Tasks

Loops let you run commands repeatedly. We’ll cover for loops (iterate over a list) and while loops (run until a condition fails).

For Loops

Iterate over a list of items (e.g., filenames, numbers):

#!/bin/bash

# Loop over numbers 1-5
for NUM in {1..5}; do
  echo "Number: $NUM"
done

# Loop over .txt files in the current directory
echo -e "\nText files in current directory:"
for FILE in *.txt; do
  echo "- $FILE"
done

While Loops

Run commands until a condition is false (e.g., read lines from a file):

#!/bin/bash

# Count from 1 to 5
COUNT=1
while [ $COUNT -le 5 ]; do
  echo "Count: $COUNT"
  COUNT=$((COUNT + 1))  # Increment count
done

# Read lines from a file (e.g., "names.txt")
echo -e "\nNames from names.txt:"
while IFS= read -r NAME; do  # IFS= prevents trimming whitespace
  echo "- $NAME"
done < "names.txt"  # Input file

Functions: Reusable Code Blocks

Functions let you group commands into reusable blocks. Define them once and call them multiple times.

Syntax

function greet {
  echo "Hello, $1!"  # $1 = first argument passed to the function
}

# Call the function
greet "Alice"  # Output: Hello, Alice!
greet "Bob"    # Output: Hello, Bob!

Example: File Backup Function

#!/bin/bash

# Function to backup a file
backup_file() {
  local FILE="$1"  # Local variable (only visible in the function)
  if [ -f "$FILE" ]; then
    cp "$FILE" "$FILE.bak"  # Create backup with .bak extension
    echo "Backed up $FILE to $FILE.bak"
  else
    echo "Error: $FILE does not exist."
    return 1  # Return non-zero exit code (error)
  fi
}

# Call the function with a user-provided file
read -p "Enter file to backup: " FILE
backup_file "$FILE"

Common Practices

  • Comment Liberally: Use # to explain complex logic. For example:
    # Check if the input file exists before processing
    if [ -f "$INPUT_FILE" ]; then ...
  • Use set -e for Error Handling: Add set -e at the top of your script to exit immediately if any command fails (avoids silent errors):
    #!/bin/bash
    set -e  # Exit on error
  • Modularize with Functions: Break large scripts into functions for readability.

Best Practices

  1. Always Use the Shebang Line: Start with #!/bin/bash (not #!/bin/sh, which may use a limited shell).
  2. Quote Variables: Use "$VAR" instead of $VAR to handle spaces in filenames/strings:
    # Bad: Fails if $FILE has spaces
    cat $FILE  
    
    # Good: Works with spaces
    cat "$FILE"
  3. Avoid Hard-Coded Paths: Use variables for paths (e.g., BACKUP_DIR="/tmp/backups").
  4. Test with shellcheck: Use the shellcheck tool to catch syntax errors and bad practices:
    sudo apt install shellcheck  # Install (Debian/Ubuntu)
    shellcheck your_script.sh    # Lint the script
  5. Version Control: Store scripts in Git to track changes and revert mistakes.

Troubleshooting Scripts

Common issues and fixes:

  • Permission Denied: Ensure the script has execute permission (chmod +x script.sh).
  • Syntax Errors: Check for missing spaces in if conditions (e.g., [ $a -eq $b ] needs spaces around [ and ]).
  • Undefined Variables: Use set -u in your script to exit if an undefined variable is used:
    #!/bin/bash
    set -u  # Exit on undefined variable
    echo $UNDEFINED_VAR  # Fails with "UNDEFINED_VAR: unbound variable"
  • Debugging: Run the script with bash -x to print each command as it executes (debug mode):
    bash -x your_script.sh

Conclusion

You now have the foundational skills to write Linux command line scripts! From simple “Hello World” programs to complex automation with variables, loops, and conditionals, shell scripting is a powerful tool for Linux users.

To level up, explore advanced topics like:

  • Command-line arguments ($1, $2, etc., for script inputs).
  • Arrays and associative arrays.
  • case statements (for multiple condition checks).
  • Error handling with trap (clean up resources on exit).

Happy scripting!

References