Your First Script
Your First Script#
Concepts#
What Is a Shell Script?#
A shell script is a text file containing a sequence of commands that the shell executes in order. Instead of typing commands one by one, you write them in a file and run the file. Scripts automate repetitive tasks, glue commands together, and can include logic (decisions, loops).
The Shebang#
The first line of every script should be the shebang (also called hashbang):
#!/bin/bash
This tells the system which program should interpret the script. Without it, the system might use a different shell (like sh or dash), which may not support all Bash features.
Common shebangs:
| Shebang | Interpreter |
|---|---|
#!/bin/bash |
Bash (use this for this course) |
#!/bin/sh |
POSIX shell (more portable, fewer features) |
#!/usr/bin/env bash |
Bash, found via PATH (more portable across systems) |
#!/usr/bin/env python3 |
Python 3 |
Debian note: On Debian,
/bin/shisdash(a minimal, fast POSIX shell), NOT Bash. Scripts using Bash-specific features (like[[, arrays,{1..10}) must use#!/bin/bash, not#!/bin/sh.
Creating and Running a Script#
Step 1: Write the script#
nano ~/myscript.sh
#!/bin/bash
echo "Hello, $(whoami)!"
echo "Today is $(date)"
echo "You are in: $(pwd)"
Step 2: Make it executable#
chmod +x ~/myscript.sh
Step 3: Run it#
# Using the path
~/myscript.sh
# Or explicitly with bash
bash ~/myscript.sh
# From the current directory
cd ~
./myscript.sh
Why ./? When you type a command without a path, the shell looks for it in directories listed in $PATH. Your current directory is usually not in $PATH for security reasons. ./ explicitly means “this directory.”
Script Structure#
A well-written script follows this structure:
#!/bin/bash
# Description: Brief description of what this script does
# Author: Your Name
# Date: 2024-10-15
# --- Configuration / Variables ---
LOGFILE="/var/log/myapp.log"
MAX_RETRIES=3
# --- Functions (if any) ---
# (covered in lesson 05)
# --- Main Logic ---
echo "Starting..."
# ... commands ...
echo "Done."
Comments#
# This is a comment — the shell ignores it
echo "This runs" # Inline comment — everything after # is ignored
Comments are essential for explaining why (not what) your code does.
Exit Codes#
Every command returns an exit code (also called return code or exit status):
0= success- Non-zero (
1to255) = failure
# Check the exit code of the last command
ls /etc/hosts
echo $?
# 0 (success)
ls /nonexistent
echo $?
# 2 (failure — file not found)
$? is a special variable that holds the exit code of the last command.
Your scripts should also set exit codes:
#!/bin/bash
# Exit with success
exit 0
# Exit with failure
exit 1
If no exit statement is given, the script exits with the exit code of the last command executed.
Running Commands in Sequence#
# Semicolon — run regardless of success/failure
command1 ; command2
# AND (&&) — run command2 only if command1 succeeds
command1 && command2
# OR (||) — run command2 only if command1 fails
command1 || command2
Examples:
# Update and install, but only install if update succeeds
sudo apt update && sudo apt install -y nginx
# Try to cd into a directory; if it fails, print an error
cd /some/dir || echo "Directory does not exist!"
# Common pattern: do something or exit
cd /important/directory || exit 1
Reading User Input#
#!/bin/bash
echo -n "What is your name? "
read name
echo "Hello, $name!"
# Read with a prompt (no echo needed)
read -p "Enter your age: " age
echo "You are $age years old."
# Read silently (for passwords)
read -sp "Enter password: " password
echo "" # newline after hidden input
echo "Password received (${#password} characters)."
# Read with a timeout
read -t 5 -p "Enter something (5 seconds): " response || echo "Timed out!"
# Read with a default (using parameter expansion)
read -p "Port [8080]: " port
port=${port:-8080}
echo "Using port: $port"
Script Arguments#
Scripts can receive arguments from the command line:
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "All arguments: $@"
echo "Number of arguments: $#"
./myscript.sh hello world
# Script name: ./myscript.sh
# First argument: hello
# Second argument: world
# All arguments: hello world
# Number of arguments: 2
| Variable | Meaning |
|---|---|
$0 |
Script name/path |
$1 to $9 |
Positional arguments 1-9 |
${10} |
Argument 10+ (braces required) |
$@ |
All arguments (as separate words) |
$* |
All arguments (as a single string) |
$# |
Number of arguments |
$$ |
PID of the script itself |
$! |
PID of the last background process |
$? |
Exit code of the last command |
Lab#
Exercise 1: Your First Script#
mkdir -p ~/lab/scripts
cd ~/lab/scripts
cat > hello.sh << 'SCRIPT'
#!/bin/bash
# My first shell script
echo "Hello, $(whoami)!"
echo "Today is $(date '+%A, %B %d, %Y')"
echo "Uptime: $(uptime -p)"
SCRIPT
chmod +x hello.sh
./hello.sh
Exercise 2: Exit Codes#
cd ~/lab/scripts
cat > exitcodes.sh << 'SCRIPT'
#!/bin/bash
# Demonstrating exit codes
echo "Trying to list /etc/hosts..."
ls /etc/hosts
echo "Exit code: $?"
echo ""
echo "Trying to list /nonexistent..."
ls /nonexistent
echo "Exit code: $?"
echo ""
echo "Using && and ||:"
ls /etc/hosts && echo "Success!" || echo "Failed!"
ls /nonexistent && echo "Success!" || echo "Failed!"
SCRIPT
chmod +x exitcodes.sh
./exitcodes.sh
Exercise 3: Script Arguments#
cd ~/lab/scripts
cat > greet.sh << 'SCRIPT'
#!/bin/bash
# Greet a user by name
if [ $# -eq 0 ]; then
echo "Usage: $0 <name>"
exit 1
fi
echo "Hello, $1!"
echo "This script received $# argument(s): $@"
SCRIPT
chmod +x greet.sh
./greet.sh
./greet.sh Alice
./greet.sh Alice Bob Carol
Exercise 4: Reading Input#
cd ~/lab/scripts
cat > interactive.sh << 'SCRIPT'
#!/bin/bash
# Interactive script
read -p "What is your name? " name
read -p "What is your favorite color? " color
read -p "How old are you? " age
echo ""
echo "Summary:"
echo " Name: $name"
echo " Color: $color"
echo " Age: $age"
SCRIPT
chmod +x interactive.sh
./interactive.sh
Exercise 5: A Practical Script — System Info#
cd ~/lab/scripts
cat > sysinfo.sh << 'SCRIPT'
#!/bin/bash
# Display system information
echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "OS: $(grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')"
echo "Kernel: $(uname -r)"
echo "Architecture: $(uname -m)"
echo "Uptime: $(uptime -p)"
echo "Users logged: $(who | wc -l)"
echo ""
echo "=== Memory ==="
free -h | head -2
echo ""
echo "=== Disk Usage ==="
df -h / | tail -1 | awk '{printf "Root filesystem: %s used of %s (%s)\n", $3, $2, $5}'
echo ""
echo "=== Top 5 Processes by Memory ==="
ps aux --sort=-%mem | head -6
SCRIPT
chmod +x sysinfo.sh
./sysinfo.sh
Clean Up#
Keep the ~/lab/scripts/ directory — you will use it in the next lessons.
Review#
1. What is the shebang and why is it important?
The shebang (#!/bin/bash) is the first line of a script. It tells the system which interpreter to use. Without it, the system may use a different shell that does not support Bash-specific features.
2. How do you make a script executable?
chmod +x script.sh. Then run it with ./script.sh (from the current directory) or /full/path/to/script.sh.
3. Why do you need `./` to run a script in the current directory?
The current directory is not in $PATH by default (for security). ./ explicitly tells the shell to look in the current directory.
4. What does `$?` contain?
The exit code of the last executed command. 0 means success; any non-zero value means failure.
5. What is the difference between `&&` and `||`?
&& runs the next command only if the previous one succeeded (exit code 0). || runs the next command only if the previous one failed (non-zero exit code).
6. How do you access the first argument passed to a script?
$1. The second argument is $2, all arguments are $@, and the count of arguments is $#.
7. Why does `/bin/sh` behave differently on Debian vs Ubuntu?
On Debian, /bin/sh is dash (a minimal POSIX shell). On Ubuntu, /bin/sh is also dash (not Bash). Scripts using Bash features (arrays, [[, brace expansion) must use #!/bin/bash, not #!/bin/sh.
Previous: Logs and journalctl | Next: Variables and Quoting