|$ curl https://forge-ai.dev/api/markdown?path=docs/linux/shell-scripting
$cat docs/shell-scripting.md
updated Recently·28 min read·published
Shell Scripting
Introduction
Shell scripts glue together command-line tools to automate backups, deployments, testing, and system administration. Bash is the default shell on most Linux systems and the lingua franca of automation.
Hello World
hello.sh
Bash
| 1 | #!/usr/bin/env bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | echo "Hello, ForgeLearn!" |
| 5 | |
| 6 | # Make executable and run |
| 7 | # chmod +x script.sh |
| 8 | # ./script.sh |
✓
best practice
set -euo pipefail is the safety harness: exit on error (-e), treat unset variables as errors (-u), and fail pipelines on any error (-o pipefail).
Variables
variables.sh
Bash
| 1 | name="Alice" |
| 2 | echo "Hello, $name" |
| 3 | echo "Hello, ${name}!" |
| 4 | |
| 5 | # Command substitution |
| 6 | now=$(date +%Y-%m-%d) |
| 7 | echo "Today is $now" |
| 8 | |
| 9 | # Default values |
| 10 | port="${PORT:-3000}" |
| 11 | name="${NAME:?NAME is required}" |
| 12 | |
| 13 | # Arithmetic |
| 14 | sum=$((3 + 4)) |
| 15 | echo "$sum" |
Conditionals
conditionals.sh
Bash
| 1 | file="data.txt" |
| 2 | |
| 3 | if [[ -f "$file" ]]; then |
| 4 | echo "File exists" |
| 5 | elif [[ -d "$file" ]]; then |
| 6 | echo "Directory exists" |
| 7 | else |
| 8 | echo "Not found" |
| 9 | fi |
| 10 | |
| 11 | # String comparison |
| 12 | if [[ "$USER" == "root" ]]; then |
| 13 | echo "Running as root" |
| 14 | fi |
| 15 | |
| 16 | # Numeric comparison |
| 17 | if (( count > 0 )); then |
| 18 | echo "Count is positive" |
| 19 | fi |
Loops
loops.sh
Bash
| 1 | # For loop over list |
| 2 | for name in alice bob carol; do |
| 3 | echo "Hello, $name" |
| 4 | done |
| 5 | |
| 6 | # For loop over files |
| 7 | for file in *.log; do |
| 8 | echo "Processing $file" |
| 9 | done |
| 10 | |
| 11 | # While loop |
| 12 | i=0 |
| 13 | while (( i < 5 )); do |
| 14 | echo "$i" |
| 15 | ((i++)) |
| 16 | done |
| 17 | |
| 18 | # Read lines from command output |
| 19 | while read -r line; do |
| 20 | echo "$line" |
| 21 | done < input.txt |
Functions
functions.sh
Bash
| 1 | #!/usr/bin/env bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | greet() { |
| 5 | local name="$1" |
| 6 | echo "Hello, $name" |
| 7 | } |
| 8 | |
| 9 | log() { |
| 10 | local level="$1" |
| 11 | local message="$2" |
| 12 | echo "[${level}] $(date -Iseconds) $message" >&2 |
| 13 | } |
| 14 | |
| 15 | result=$(greet "Alice") |
| 16 | echo "$result" |
| 17 | log "INFO" "Script started" |
Arguments
arguments.sh
Bash
| 1 | #!/usr/bin/env bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | input="$1" |
| 5 | output="${2:-output.txt}" |
| 6 | |
| 7 | if [[ $# -lt 1 ]]; then |
| 8 | echo "Usage: $0 <input> [output]" >&2 |
| 9 | exit 1 |
| 10 | fi |
| 11 | |
| 12 | echo "Input: $input" |
| 13 | echo "Output: $output" |
Best Practices
- Always quote variables: "$var" instead of $var.
- Use set -euo pipefail unless you have a reason not to.
- Prefer [[ ]] over [ ] for conditionals.
- Use local inside functions to avoid leaking variables.
- Validate inputs and print usage messages.
- For complex logic, consider Python instead of Bash.