Handbooks / Linux / Chapter 3

Shell Scripting

7 pages · ~5 min✓ Reviewed

Builds on Text Processing. Next up: Processes and Signals.

LinuxPage 1

Shebang and Variables

1. Shebang and Running

  • Shebang is line 1 only; the kernel reads it on exec
  • #!/usr/bin/env bash picks bash from PATH
  • #!/bin/bash is a fixed path
RunShebangRuns inVars persist
chmod +x s.sh && ./s.shusedchildno
bash s.shignoredchildno
source s.shignoredcurrent shellyes

2. Variables

bash
name=world          # no spaces around =
greet=${name:-hi}   # fallback if unset or empty
now=$(date +%F)     # command substitution
n=$((n+1))          # arithmetic
readonly MAX=3
export MODE=dev
  • readonly blocks reassignment; export shows it to child processes
  • Untyped strings; global unless local in a function

3. Interview Point

  • Why env bash? It finds bash on PATH, so it works where bash is not in /bin
  • ./s.sh vs source? ./ runs a child shell; source runs in the current one
  • Why does x = 5 fail? The spaces make x a command with two arguments
LinuxPage 2

Quoting and Expansion Rules

1. Quotes and Splitting

  • Unquoted $var is word-split and glob-expanded
  • f='my file.txt'; rm $f gets two args
  • Always write "$var"
  • "..." keeps $var, $(cmd); \ escapes only $ backtick " \
  • '...' is fully literal
FormPrints
echo "$HOME"the path
echo '$HOME'$HOME
"a\nb"stays literal

2. Forwarding Arguments

FormExpands to
"$@"each arg as a separate word: forward with this
"$*"all args joined into one string

3. Tests

  • [[ ]]: left side needs no quotes; == is pattern match
  • [ ]: unquoted empty var gives a syntax error

4. Interview Point

  • rm $dir/* with a space or empty $dir? Splits into extra args; empty becomes rm /*
  • Single vs double quotes? Single is literal; double expands $ and $(cmd)
  • "$@" vs "$*"? Separate words vs one string
Remember

Quote every $var; forward arguments with "$@".

LinuxPage 3

Conditionals and Loops

1. Tests Run Commands

  • if cmd; then ...; elif ...; else ...; fi tests exit status
  • 0 = true, nonzero = false
  • [ ] and [[ ]] are just commands returning a status
[ ][[ ]]
OriginPOSIXbash-native
Quotingneededno splitting
And / or-a -o&& ||
Regexno=~

2. Common Tests

  • -f file, -d dir, -e exists
  • -z empty string, -n non-empty
  • -eq -lt -gt integers; == strings

3. Loops

bash
for f in *.log; do echo "$f"; done
for ((i=0;i<5;i++)); do echo "$i"; done
while read -r line; do echo "$line"; done < file
until cmd; do sleep 1; done

break leaves the loop, continue skips to the next pass

4. Interview Point

  • [ ] vs [[ ]]? POSIX command vs bash keyword: no splitting, &&, =~
  • Why while read -r? for over $(cat f) splits on spaces; -r keeps backslashes
  • if grep -q x f tests? grep's exit status: 0 when a match is found
LinuxPage 4

Functions, Arguments and Exit Status

1. Functions and Arguments

  • Define name() { ...; }, call it like a command
  • Inside: $1 $2 ... and $#
  • local x=1 keeps x out of the global scope
ItemMeans
$0script name
$1..$9positional
${10}needs braces
$# / $@count / all args
shiftdrops $1
${1:-fallback}default if unset

2. Exit Status

  • return n and exit n set a status 0-255
  • return leaves the function; exit ends the script
  • $? = last status, overwritten by the next command
  • exit 0 = success, anything else = failure
  • Functions return a status; data goes out via stdout: result=$(fn)
  • Test success with if fn; then
bash
greet() { echo "hi $1"; }
msg=$(greet bob)
check() { [[ -f "$1" ]]; }
if check /etc/passwd; then echo ok; fi
echo "$msg"
output
ok
hi bob
LinuxPage 5

Strict Mode and Cron

1. Strict mode

FlagEffect
set -eexit on first failing command
set -uunset variable is an error
set -o pipefailpipeline fails if any stage fails
  • Put set -euo pipefail at the top of the script
  • set -e skips if/while conditions
  • Also skips any &&/|| command except the last: false && x lives, true && false exits
  • Without pipefail, false | true returns 0 and hides the failure
bash
false | true; echo $?
set -o pipefail
false | true; echo $?
output
0
1

2. Cron

  • Fields: min hour dom mon dow command
  • crontab -e edits, crontab -l lists
ScheduleRuns
*/5 * * * *every 5 minutes
0 2 * * 102:00 on Mondays
Works in terminal, fails in cron

Cron has a minimal PATH and no login environment. Use absolute paths, set PATH= at the top, and add >> /var/log/job.log 2>&1 or output mail is lost.

LinuxPage 6

Bash Scripting Cheat Sheet

1. Header and Specials

bash
#!/usr/bin/env bash
set -euo pipefail
v="${1:-x}"
now=$(date +%F)
n=$(( $# + 1 ))

default, substitution, math

  • Run: chmod +x s.sh, then ./s.sh
  • Always quote: "$var"
VarMeaning
$?last status
$#arg count
$@all args
$0script name
$$PID

2. Tests, Loops, Functions

SyntaxMeaning
[[ -f f ]]file exists
[[ -z "$v" ]]empty string
[[ $a -lt $b ]]int less than
for x in a b; doloop a list
while read -r l; doloop lines
f() { local x=$1; }function, local var

3. Cron

  1. mmin
  2. hhour
  3. domday
  4. monmonth
  5. dowweekday
  6. cmdcommand
  • */5 * * * * every 5 minutes
  • 0 2 * * 1 02:00 Mondays
  • crontab -e edit, -l list
Cron

Absolute paths; add >> log 2>&1

LinuxPage 7

Check yourself

f='my file.txt'; rm $f — what does rm receive, and what is the fix?
  • Two arguments: my and file.txt
  • Unquoted expansion is word-split
  • Fix: rm "$f"
What does false | true; echo $? print without pipefail, and with it?
  • 0 without: the pipeline takes the last stage's status
  • 1 with set -o pipefail: any failing stage fails the pipe
Under set -e, [ -f nope ] && echo yes is followed by echo done. Does the script stop?
  • No, it prints done
  • A failing non-last command in an && list does not trigger set -e