Bash #
The default shell on most Linux systems and a capable scripting language. Commands are words separated by spaces; everything else is quoting, expansion, and control flow.
Navigation & files #
pwd # print working directory
cd /var/log # change directory ( cd - jumps back )
ls -lah # long list, all files, human sizes
find . -name '*.log' -type f # search by name, files only
cp -r src/ bak/ # copy a directory
mv old.txt new.txt # move or rename
rm -rf build/ # delete recursively, no prompt (careful!)
tail -f app.log # follow a file as it grows
Variables & expansion #
name="turtle" # no spaces around =
count=42
echo "$name has $count items" # double quotes expand variables
echo '$name stays literal' # single quotes do not expand
greeting="Hello, ${name}!" # braces disambiguate the name
readonly PI=3.14 # constant
export PATH="$HOME/bin:$PATH" # export to child processes
unset count # remove a variable
Parameter expansion #
file="report.tar.gz"
echo "${file%.gz}" # report.tar (strip shortest suffix)
echo "${file%%.*}" # report (strip longest suffix)
echo "${file#*.}" # tar.gz (strip shortest prefix)
echo "${name:-guest}" # default if unset/empty
echo "${name:=guest}" # default AND assign
echo "${#name}" # length of the value
echo "${path//\//_}" # replace all / with _
Command substitution & arithmetic #
today=$(date +%F) # capture command output
files=$(ls | wc -l)
echo "There are $files files as of $today"
n=$(( (3 + 4) * 2 )) # integer arithmetic
(( count++ )) # increment in place
echo $(( RANDOM % 6 + 1 )) # dice roll 1-6
Tests & conditionals #
if [[ -f "$config" ]]; then # file exists and is regular
source "$config"
elif [[ -d "$config" ]]; then # is a directory
echo "expected a file"
else
echo "no config"
fi
[[ -z "$x" ]] # string is empty
[[ -n "$x" ]] # string is non-empty
[[ "$a" == "$b" ]] # string equality
[[ "$n" -gt 10 ]] # numeric: -eq -ne -lt -le -gt -ge
[[ "$s" =~ ^[0-9]+$ ]] # regex match
cmd && echo ok || echo failed # run on success / on failure
Loops #
for f in *.txt; do # glob expands to matching files
echo "processing $f"
done
for i in {1..5}; do echo "$i"; done # brace range
for ((i = 0; i < 5; i++)); do echo "$i"; done # C-style
while read -r line; do # read a file line by line
echo "$line"
done < input.txt
until ping -c1 host &>/dev/null; do sleep 1; done
Functions #
greet() {
local who="${1:-world}" # local keeps it out of global scope
echo "Hello, $who"
return 0 # exit status, not a value
}
greet "turtle" # call with arguments
result=$(greet) # capture echoed output
# $1 $2 ... positional args $# count $@ all args $? last status
Arrays #
fruits=(apple banana cherry) # indexed array
echo "${fruits[0]}" # apple
echo "${fruits[@]}" # all elements
echo "${#fruits[@]}" # length
fruits+=(date) # append
for f in "${fruits[@]}"; do echo "$f"; done
declare -A color # associative array (bash 4+)
color[sky]=blue
echo "${color[sky]}"
Redirection & pipes #
cmd > out.txt # stdout to file (overwrite)
cmd >> out.txt # append
cmd 2> err.txt # stderr to file
cmd > out.txt 2>&1 # both to one file
cmd &> all.txt # shorthand for both
cmd < in.txt # stdin from file
a | b | c # pipe stdout through a chain
diff <(sort a) <(sort b) # process substitution
cmd | tee log.txt # to screen and file
Text processing #
grep -rn "TODO" src/ # recursive, with line numbers
grep -i "error" log | wc -l # case-insensitive count
sed 's/foo/bar/g' file # substitute all on each line
awk '{ print $1, $3 }' data # print fields 1 and 3
awk -F, '{ sum += $2 } END { print sum }' nums.csv
cut -d: -f1 /etc/passwd # first colon-delimited field
sort file | uniq -c | sort -rn # frequency count, descending
Safe scripting #
#!/usr/bin/env bash
set -euo pipefail # exit on error, unset var, pipe failure
IFS=$'\n\t' # safer word splitting
trap 'echo "cleaning up"; rm -f "$tmp"' EXIT # run on exit
tmp=$(mktemp)
main() {
[[ $# -ge 1 ]] || { echo "usage: $0 <file>" >&2; exit 1; }
process "$1"
}
main "$@" # forward all arguments
Jobs & history #
long_task & # run in the background
jobs # list background jobs
fg %1 # bring job 1 to foreground
kill %1 # signal a job
nohup ./run.sh & # survive logout
Ctrl-C Ctrl-Z # interrupt / suspend current job
!! !$ # last command / its last argument
history | grep ssh # search command history