opscape

Shell builtins cheat sheet

The most useful shell builtins commands on one printable page, every line checked by running it.

alias Define a short name that the shell replaces with a longer command line, or list the aliases already defined.

shopt -s expand_aliases alias lt='ls -t' lt data | head -n 3
Define and use an alias output
alias ll='ls -l' la='ls -A' gs='git status' alias
List the defined aliases output
errors() { grep -c "ERROR" "$@"; } errors logs/app.log errors -h logs/app.log logs/error.log
Use a function when you need arguments output
shopt -s expand_aliases alias ls='ls -F' ls scripts \ls scripts command ls scripts
Bypass an alias for one command output

command Run a command while skipping functions and aliases of the same name, or check whether a command exists with -v.

for tool in jq curl kubectl; do if command -v "$tool" >/dev/null 2>&1; then echo "$tool: found at $(command -v "$tool")" else echo "$tool: not installed" fi done
Check whether a program is installed output
grep() { command grep --color=never -n "$@"; } grep TODO notes.txt src/app.py
Wrap a command in a function of the same name output
ls() { echo "(ls is overridden)"; } ls scripts command ls scripts
Bypass a function or alias for one call output

declare Create variables with attributes (array, associative array, integer, read-only, exported), or show how variables are defined.

name="Alice Moreau" empty="" list=(one "two words" three) declare -p name empty list
Inspect a variable exactly with -p output
declare -A port=([web]=80 [db]=5432) port[cache]=6379 for k in "${!port[@]}"; do echo "$k -> ${port[$k]}"; done | sort
Create an associative array with -A output
add_item() { declare -n list=$1 list+=("$2") } fruits=(apple) add_item fruits banana add_item fruits cherry declare -p fruits
Refer to another variable by name with -n output
count=1 f() { declare count=99; echo "inside: $count"; } g() { declare -g count=42; } f; echo "after f: $count" g; echo "after g: $count"
declare inside a function makes a local variable output

eval Join the arguments into a string and run it as shell code, with a second round of parsing and expansion.

bash greet-unsafe.sh 'Alice' bash greet-unsafe.sh 'Alice; touch HACKED; ls H*'
Why eval is dangerous output
args=(-c -i) pattern='error' args+=(-- "$pattern" "logs/app.log") grep "${args[@]}"
Build commands with an array instead output
db_host=db01; web_host=web01 role=db var="${role}_host" echo "unsafe: $(eval "echo \$$var")" echo "safe: ${!var}"
Read a variable whose name is in another variable output
name='Alice; touch HACKED' eval "echo Hello, $(printf '%q' "$name")" ls HACKED
Quote safely with %q when you must generate code output

exec Replace the shell with another program, or, with only redirections, change where the shell's own input and output go.

echo "shell pid: $$" exec bash -c 'echo "new program pid: $$"' echo "this line never runs"
Replace the shell with another program output
bash run-greet.sh Alice echo "wrapper exit status: $?"
End a wrapper script with exec output
bash job.sh echo "--- run.log:" cat run.log
Send the rest of a script's output to a log file output
exec 3>&1 exec >capture.txt echo "written to the file" exec 1>&3 3>&- echo "back on the terminal" echo "file contains: $(cat capture.txt)"
Redirect temporarily and restore the original output output

export Mark shell variables (or bash functions) to be passed to every program the shell starts.

COLOR=blue bash -c 'echo "child sees COLOR=[$COLOR]"' export COLOR bash -c 'echo "child sees COLOR=[$COLOR]"'
Make a variable visible to child processes output
export API_URL=http://localhost:8080 RETRIES=3 env | grep -E '^(API_URL|RETRIES)='
Set and export in one step output
TZ=Asia/Tokyo date +%H:%M-%Z date +%H:%M-%Z echo "TZ in this shell: [${TZ-unset}]"
Set a variable for a single command output
mkdir -p ~/bin printf '#!/bin/sh\necho "hello from ~/bin"\n' > ~/bin/hello chmod +x ~/bin/hello export PATH="$HOME/bin:$PATH" hello echo "$PATH" | tr : '\n' | head -n 3
Add a directory to PATH output

false Do nothing and exit with status 1 (failure).

false; echo "exit: $?"
Check its exit status output
false && echo "and: ran" || echo "or: ran"
Watch && and || react to failure output
set -e echo "step 1" false echo "step 2"
Stop a script at the first failure output

getopts Parse short command-line options such as -v and -o FILE in a script, one option per call, inside a while loop.

chmod +x backup.sh ./backup.sh -v -o /tmp/out data logs ./backup.sh data
A complete script with getopts output
set -- -v -o out.txt input.csv while getopts 'vo:' opt; do echo "got -$opt, OPTIND now $OPTIND"; done shift $((OPTIND - 1)) echo "remaining: $*"
Watch OPTIND advance output
f() { local OPTIND opt while getopts 'q' opt; do echo " option -$opt"; done shift $((OPTIND - 1)); echo " args: $*" } echo "first call:"; f -q one echo "second call:"; f -q two
Reset OPTIND when using getopts in a function output

local Declare variables inside a function that disappear when it returns, leaving any global of the same name untouched.

name="global" without() { name="changed by without"; } with() { local name="changed by with"; echo "inside with: $name"; } with; echo "after with: $name" without; echo "after without: $name"
Keep a function's variables to itself output
show() { echo "show sees level=$level"; } outer() { local level=outer; show; } level=global outer show
Locals are visible to called functions output
bad() { local out=$(grep -c CRITICAL logs/app.log); echo "status after local=: $?"; } good() { local out; out=$(grep -c CRITICAL logs/app.log); echo "status after split: $?"; } bad; good
local hides the exit status of command substitution output

mapfile Read lines from standard input into an indexed array, one line per element (also called readarray).

mapfile -t tasks < todo.txt echo "${#tasks[@]} tasks" echo "first: ${tasks[0]}" echo "last: ${tasks[-1]}"
Read a file into an array output
mapfile -t errors < <(grep ERROR logs/app.log) echo "found ${#errors[@]} errors" printf ' > %s\n' "${errors[@]}"
Capture command output with process substitution output
mapfile -t -s 1 -n 3 rows < data/users.csv printf '%s\n' "${rows[@]}"
Skip a header and limit the count with -s and -n output
touch 'photos/new line.png' mapfile -t -d '' pics < <(find photos -name '*.png' -print0) echo "${#pics[@]} files" printf '<%s>\n' "${pics[@]}" | sort
Collect file names safely with -d '' output

popd Remove the top directory from the directory stack and change back to the next one.

pushd data >/dev/null pushd ../logs >/dev/null dirs -v popd popd
Return to the previous directory output
pushd /tmp >/dev/null touch scratch.$$ popd >/dev/null echo "back in $PWD"
Use popd quietly in a script output
pushd /tmp >/dev/null; pushd /etc >/dev/null; pushd /usr >/dev/null dirs -v popd +2 >/dev/null echo "after popd +2, still in $PWD:" dirs -v
Remove a specific entry with +N output

pushd Change to a directory and remember the previous one on a stack, so popd can return to it.

pushd data pwd pushd ../logs dirs -v
Change directory and remember where you were output
pushd /etc >/dev/null echo "now in $PWD" popd >/dev/null echo "back in $PWD"
Go somewhere and come back with popd output
pushd src >/dev/null pushd >/dev/null; echo "1: $PWD" pushd >/dev/null; echo "2: $PWD" pushd >/dev/null; echo "3: $PWD"
Toggle between two directories output

read Read one line from standard input and split it into shell variables.

while read -r host ip; do echo "$host is at $ip" done < data/hosts.txt
Process a file line by line output
tail -n +2 data/users.csv | head -n 3 | while IFS=, read -r id name email dept joined; do printf '%-14s %s\n' "$name" "$dept" done
Split on a different separator with IFS output
printf ' indented line \n' > in.txt read -r line < in.txt; echo "[$line]" IFS= read -r line < in.txt; echo "[$line]"
Preserve leading spaces with IFS= output
read -r count < <(grep -c . todo.txt) echo "count=$count" read -r first rest <<< "a b c" echo "first=$first rest=$rest"
Read command output into variables in the current shell output
find photos -name '*.png' -print0 | sort -z | while IFS= read -r -d '' f; do echo "file: $f" done
Read NUL-separated names with -d '' output

set Turn shell options such as -e, -u, -x and pipefail on or off, and replace the positional parameters.

set -e echo "start" ls missing-file echo "never printed"
Stop a script at the first failing command with -e output
set -u backup_dir=/tmp/backups echo "cleaning ${backup_dri}" echo "not reached"
Catch typos in variable names with -u output
grep ERROR missing.log | wc -l; echo "status without pipefail: $?" set -o pipefail grep ERROR missing.log | wc -l; echo "status with pipefail: $?"
Make a pipeline fail when any part fails output
set -x name="file with spaces.txt" wc -l "$name" set +x echo "tracing is off"
Trace each command before it runs with -x output
set -- alpha "beta gamma" delta echo "count: $#" printf '[%s]\n' "$@"
Replace the positional parameters output

shift Drop the first N positional parameters ($1, $2, ...) and renumber the rest down.

set -- alpha beta gamma echo "before: \$1=$1, $# args" shift echo "after: \$1=$1, $# args, all: $*"
Drop the first argument output
bash tool.sh count todo.txt notes.txt bash tool.sh show "file with spaces.txt"
Split off a subcommand and pass the rest through output
bash deploy.sh --env=prod -n web01 web02 bash deploy.sh -e dev web01 bash deploy.sh --dry-run -- -odd-host
Parse options by hand, including long options output

source Run the commands in a file inside the current shell, so its variables, functions and directory changes stay in effect.

source ./app.env echo "$APP_NAME listens on $APP_PORT"
Load variables from a file into the current shell output
bash setdir.sh echo "after bash: pwd=$PWD greeting=[$GREETING]" source ./setdir.sh echo "after source: pwd=$PWD greeting=[$GREETING]"
Sourcing versus running a script output
source src/util.sh log "starting backup" type die | head -n 3
Use functions from a library file output
. ./.env echo "API_URL is $API_URL" source ./.env
The portable form is a dot output

test Evaluate a condition about files, strings or integers and return 0 for true or 1 for false.

if [ -f notes.txt ]; then echo "notes.txt is a regular file"; fi test -d data && echo "data is a directory" [ -e nothing.txt ] || echo "nothing.txt does not exist"
Check that a file exists output
[ 10 = 10.0 ] && echo "= equal" || echo "= different" [ 010 -eq 10 ] && echo "-eq equal" || echo "-eq different" [[ 9 > 10 ]] && echo "[[ > ]] says 9 is greater (text order)" (( 9 > 10 )) || echo "(( > )) says 9 is smaller"
String comparison versus numeric comparison output
name="" if [ $name = "admin" ]; then echo admin; else echo "not admin"; fi if [ -n $name ]; then echo "-n says non-empty"; fi if [ -n "$name" ]; then echo "never"; else echo "quoted -n says empty"; fi
An unquoted empty variable breaks [ output
file="file with spaces.txt" [ -f $file ] || echo "[ with unquoted var: failed" [[ -f $file ]] && echo "[[ with unquoted var: works" [[ $file == *.txt ]] && echo "pattern match: ends in .txt" [[ $file =~ ^file\ (.*)\.txt$ ]] && echo "regex group: ${BASH_REMATCH[1]}"
[[ ]] does not split variables and supports patterns output
for p in notes.txt data link-to-notes scripts/greet.sh missing; do printf '%-17s' "$p" [ -e "$p" ] && printf ' exists' || printf ' missing' [ -f "$p" ] && printf ' file' [ -d "$p" ] && printf ' dir' [ -L "$p" ] && printf ' symlink' [ -x "$p" ] && printf ' executable' echo done
Distinguish files, directories and links output

trap Run a command when the shell receives a signal or exits, typically to clean up temporary files.

bash job.sh echo "exit status: $?"
Clean up a temporary file whatever happens output
bash report.sh data bash report.sh nope; echo "caller sees $?"
See the exit code inside an EXIT trap output
trap 'echo "caught SIGINT, stopping cleanly"; exit 130' INT echo "working..." kill -INT $$ echo "not reached"
Handle Ctrl-C (SIGINT) output
trap 'echo "error: status $? at line $LINENO: $BASH_COMMAND" >&2' ERR grep -q kiwi data/fruits.txt ls data > /dev/null false echo "the ERR trap does not stop the shell"
Report the failing line with an ERR trap output

true Do nothing and exit with status 0 (success).

true; echo "exit: $?"
Check its exit status output
n=0 while true; do n=$((n + 1)) [ "$n" -ge 3 ] && break done echo "stopped after $n rounds"
Loop until something breaks out output
set -e rm old-cache.tmp 2>/dev/null || true echo "still running"
Tolerate one failing command under set -e output

type Show how the shell would interpret a name, as an alias, keyword, function, builtin or file on PATH.

type ls cd if printf
Find out what a name is output
type -a echo printf test
List every match with -a output
greet() { :; } for n in greet cd if grep nosuch; do printf '%-7s %s\n' "$n" "$(type -t "$n")" done
Get a one-word answer with -t output

ulimit Show or set per-process resource limits such as open files, core size and stack size.

ulimit -n
Show the open-files limit output
echo "soft: $(ulimit -Sn) hard: $(ulimit -Hn)"
Show the soft and hard limits separately output
(ulimit -n 4096; echo "inside: $(ulimit -n)") echo "outside: $(ulimit -n)"
Raise the open-files limit for one command output
echo "core limit: $(ulimit -c)" ulimit -c 0 echo "core limit now: $(ulimit -c)"
Check and disable core dumps output

umask Show or set the mask that removes permission bits from newly created files and directories.

umask
Show the current umask output
touch new.txt; mkdir newdir ls -ld new.txt newdir
See the effect on new files and directories output
umask 077 touch secret.txt; mkdir private ls -ld secret.txt private
Create files only you can read output

unset Remove shell variables, array elements or functions, so they no longer exist rather than being empty.

name="Alice" unset name echo "name=[$name]" echo "${name-it is unset}"
Remove a variable output
ls() { echo "fake ls"; } ls scripts unset -f ls ls scripts
Remove a function with -f output
fruits=(apple banana cherry date) unset 'fruits[1]' echo "values: ${fruits[*]}" echo "indices: ${!fruits[*]}" echo "count: ${#fruits[@]}"
Remove one array element output

yes Print a line (y by default) over and over until killed or the reader stops reading.

yes | head -n 3
Print y repeatedly output
yes | rm -i *.tmp ls *.tmp
Answer every prompt of an interactive command output
yes 'GET /health 200' | head -n 1000 > requests.log wc -l requests.log sort requests.log | uniq -c
Make a file with N identical lines output