Navigation
| Command | What it does |
|---|---|
| Print the directory you are in | |
| Change directory | |
| Go up one level ( for two) | |
| Jump back to the previous directory | |
| Go home (same as ) | |
| List files | |
| List everything, hidden files included, with permissions and sizes | |
| Sizes in K and M instead of bytes | |
| Newest first | |
| / | Save the current directory, jump away, come back |
File Commands
| Command | What it does |
|---|---|
| Create an empty file, or update its timestamp | |
| Create a directory, parents included | |
| Copy a file ( for a directory) | |
| Move or rename | |
| Delete ( deletes a directory without asking) | |
| Symbolic link | |
| Print a file ( to page through it) | |
| / | First or last 20 lines ( follows a log) |
| Make a file executable (full story on the chmod cheat sheet) | |
| Change owner and group | |
| Find files by name (more on the find cheat sheet) | |
| Size of a directory | |
| Count lines |
Networking
Not bash itself, but the commands every bash cheat sheet gets used for on a server.
| Command | What it does |
|---|---|
| List interfaces and their addresses (replaces ) | |
| Routing table ( shows the route actually used) | |
| Send four pings, then stop | |
| Listening TCP and UDP ports with the process behind each (ss cheat sheet) | |
| Resolve a name ( for the reverse, dig cheat sheet) | |
| Headers only, to check a site answers (curl cheat sheet) | |
| Download quietly and fail on an HTTP error | |
| Test whether a TCP port is open | |
| Connect on a non-default port (ssh cheat sheet) | |
| Sync a directory, sending only the changes (rsync cheat sheet) |
Jobs and Processes
| Command | What it does |
|---|---|
| Run it in the background | |
| List this shell's jobs with their PIDs | |
| / | Bring job 1 back to the foreground / resume it in the background |
| Suspend what is running; it becomes a stopped job | |
| Block until every background job of this shell finishes | |
| Wait for one job ( holds the PID of the last one) | |
| Keep it running after you log out | |
| Detach job 1 from the shell without stopping it | |
| Find a process by name | |
| The same search without the extra grep line in the output | |
| Ask a process to stop ( when it will not) | |
| Kill by full command line rather than PID | |
| Kill whatever is holding port 8080 | |
| How long it took | |
| Wait five seconds (, ) | |
| Give up on it after ten seconds |
If answers "There are stopped jobs", run to see them, then to finish each one or to drop it. A second leaves anyway and kills them.
Variables
No spaces around the equals sign, and quote expansions unless you want word splitting.
| Command | What it does |
|---|---|
| Assign ( breaks: bash runs as a command) | |
| Expand it; the quotes keep spaces intact | |
| Make it visible to child processes too | |
| Remove it | |
| Constant | |
| Function-scoped variable (only valid inside a function) | |
| List exported variables | |
| , , | Built-ins: home directory, username, current directory |
| Colon-separated list of directories searched for commands | |
| , | A random number; seconds since the shell started |
If and Test Operators
Use in bash: it is safer with unquoted variables than and adds glob and regex matching.
if [[ -f "$conf" ]]; then
echo "found"
elif [[ -d /etc/nginx ]]; then
echo "directory only"
else
echo "missing"
fi| Test | True when |
|---|---|
| The path exists, whatever it is | |
| A regular file exists | |
| A directory exists | |
| The file exists and is not empty | |
| / / | Readable / writable / executable |
| The string is empty | |
| The string is not empty | |
| Strings are equal ( for not equal) | |
| Glob match; keep the pattern side unquoted | |
| Numeric compare; also | |
| Arithmetic test with normal operators |
Combine with and inside , negate with .
Case Statements
Cleaner than a stack of when one value has several possible shapes.
case "$1" in
start) systemctl start app ;;
stop) systemctl stop app ;;
restart|reload) systemctl restart app ;; # | separates alternatives
*.log) echo "that is a log file" ;;
"") echo "no argument given" ;;
*) echo "usage: $0 {start|stop|restart}" >&2; exit 2 ;;
esac- The patterns are globs, not regex, and the first match wins, so goes last as the catch-all.
- ends a branch. falls straight into the next branch, carries on testing the remaining patterns (bash 4+).
- makes the matching case-insensitive.
Loops
for f in *.log; do # every .log file here
echo "$f"
done
for i in {1..5}; do # brace range: 1 2 3 4 5
echo "$i"
done
for ((i = 0; i < 3; i++)); do # C-style counter
echo "$i"
done
while read -r line; do # a file, one line at a time
echo "$line"
done < servers.txt
until ssh -q web1 true; do # retry until it succeeds
sleep 5
doneleaves the loop, skips to the next round.
Arrays
Indexed from 0, and must be quoted or elements with spaces split apart.
arr=(one two three)
arr+=(four) # append
echo "${arr[0]}" # first element
echo "${arr[-1]}" # last element
echo "${arr[@]}" # all elements
echo "${#arr[@]}" # how many
unset 'arr[1]' # remove one (indexes keep their gaps)
for item in "${arr[@]}"; do
echo "$item"
done
declare -A port=([http]=80 [ssh]=22) # associative array, bash 4+
echo "${port[ssh]}"
for key in "${!port[@]}"; do
echo "$key -> ${port[$key]}"
doneParameter Expansion
String surgery without spawning . The keyboard is the mnemonic for the confusing pair: sits left of and cuts from the front, sits right of it and cuts from the back. One symbol takes the shortest match, doubled takes the longest. The patterns are globs, not regex.
| Expansion | What it does |
|---|---|
| if var is unset or empty | |
| Same, and assigns it to var as well | |
| Exit with the message if var is unset | |
| Length of the value | |
| Substring: 5 characters starting at position 2 | |
| Strip the shortest match from the front | |
| Strip the longest match from the front ( is the filename) | |
| Strip from the end ( becomes ) | |
| Strip the longest match from the end | |
| Replace the first match ( replaces all) | |
| / | Uppercase / lowercase the value (bash 4+) |
Arithmetic
Integers only. Bash cannot do decimals on its own.
| Command | What it does |
|---|---|
| Arithmetic in an expression; all work | |
| Increment in place (, ) | |
| Assign without any on the names inside | |
| Last index of an array | |
| A random number from 0 to 99 | |
| Decimals, since bash truncates | |
| The same job without installing bc | |
| Zero-pad a number to five digits | |
| Round for display | |
| Print with no trailing newline ( is less portable) |
is arithmetic, runs a command. A stray space between the parentheses changes which one you get.
Globbing
Bash expands these before the command ever runs, so is the shell handing a list of names.
| Pattern | Matches |
|---|---|
| Any characters, but never a leading dot | |
| Exactly one character | |
| / / | One character from a set, a range, or anything outside it |
| / / | Brace expansion; it expands even when nothing matches |
| An unmatched pattern becomes nothing instead of staying literal | |
| An unmatched pattern becomes an error | |
| Let include dotfiles | |
| Enables , which matches down through directories | |
| Match without regard to case | |
| Adds , , , and , as in |
Quote the pattern when the command should do its own matching: and both want the pattern intact, not a list of files the shell already expanded.
Redirection and Pipes
| Command | What it does |
|---|---|
| Write stdout to a file, overwriting it | |
| Append instead | |
| Write stderr to a file | |
| Both streams to one file (this order, not the reverse) | |
| Same thing, bash shorthand | |
| Throw the errors away | |
| Read stdin from a file | |
| Pipe stdout into the next command | |
| Screen and file at once ( appends) | |
| Feed a string as stdin |
Multi-line input goes through a heredoc; quote the delimiter () to stop expansion inside it.
cat > /etc/motd <<EOF
Welcome to $(hostname)
EOFCommand Substitution
| Command | What it does |
|---|---|
| Run cmd, use its output as a value | |
| Capture output into a variable | |
| Inline inside a string | |
| Capture a count (grep cheat sheet) | |
| The file's contents, faster than | |
| Arithmetic, a different thing with double parentheses | |
| Process substitution: treat output as a file |
nests cleanly, as in . Backticks do the same job but are the legacy form.
Reading Input
Always pass , or eats backslashes in the input.
| Command | What it does |
|---|---|
| Read one line into | |
| Prompt on the same line | |
| Hide what is typed | |
| Give up after ten seconds | |
| Take a single keypress, no Enter needed | |
| Split a line into an array on spaces | |
| Split on a different delimiter | |
| Read a whole file into an array (bash 4+) |
A yes/no prompt, defaulting to no:
read -rp "Deploy to production? [y/N] " ans
[[ "$ans" == [yY]* ]] || exit 0Prompting inside a loop does not work: the loop already owns stdin, so the prompt reads the next line of the file. Add to the inner .
Keyboard Shortcuts
Bash line editing is readline; these work in any readline program, psql and python included.
| Key | Action |
|---|---|
| / | Start / end of the line |
| / | Back / forward one word |
| Delete the word before the cursor | |
| Delete the word after the cursor | |
| Delete to the start of the line | |
| Delete to the end of the line | |
| Paste back whatever the last delete removed | |
| Undo the last edit | |
| Search history as you type | |
| Clear the screen | |
| Insert the last argument of the previous command | |
| Open the current line in | |
| Kill the running command | |
| Suspend it ( resumes, resumes in the background) | |
| End of input; on an empty prompt, log out | |
| Complete; press twice to list the options |
Vi Mode
The table above is emacs mode, the bash default. swaps it for vi keys; put it in , or in to change every readline program at once. You start in insert mode, so press first.
| Key | Action |
|---|---|
| / | Switch line editing to vi keys, or back to the default |
| Leave insert mode, so the keys below apply | |
| / | Insert before / after the cursor |
| / | Insert at the start / end of the line |
| / | Start / end of the line |
| / | Forward / back one word |
| / | Delete a word / everything to the end of the line |
| Clear the whole line | |
| Change a word: delete it and drop into insert mode | |
| / | Previous / next command from history |
| then | Search history backwards, then repeat the search |
| Open the current line in (vim cheat sheet) |
and still work from insert mode, so history search does not go away.
History
| Command | What it does |
|---|---|
| Numbered list of past commands | |
| Rerun the last command | |
| Rerun it with sudo | |
| Rerun entry 42 | |
| Rerun the most recent command starting with ssh | |
| The last argument of the previous command | |
| Rerun the last command with replaced by | |
| Search history | |
| Clear this session's history |
Worth setting in :
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTTIMEFORMAT='%F %T '
export HISTCONTROL=ignoredups:ignorespace # skip duplicates; a leading space hides a commandStartup Files
| File | When bash reads it |
|---|---|
| Every interactive non-login shell: aliases, functions, prompt, shopt | |
| Login shells only (ssh, a console login, macOS Terminal) | |
| Fallback login file, also read by sh and other shells | |
| When a login shell exits | |
| , | System-wide, read before the per-user files |
| Readline settings, so keybindings and vi mode go here | |
| Reload after editing ( is the same command) | |
| Check which kind of shell you are in |
- A login shell does not read . That is why most files contain , and why a you set in one file is missing in the other.
- These files are sourced, not executed, so they need no and no shebang.
- starts a non-interactive shell. Bash does read there, but most distro copies open with a line that returns early when the shell is not interactive, so anything below it never applies to remote commands.
- A prompt that redraws over itself usually means unescaped colour codes in . Wrap every escape sequence in and so bash does not count it towards the line width.
Exit Codes
| Command | What it does |
|---|---|
| Exit code of the last command; 0 means success | |
| Run next only if cmd succeeded | |
| Run fallback only if cmd failed | |
| Leave the script with that code | |
| Stop the script at the first failing command | |
| Treat unset variables as errors | |
| A pipeline fails if any stage fails, not just the last | |
| All three; the usual first line after the shebang | |
| Run cleanup when the script exits, however it exits |
Codes worth recognising: found but not executable, command not found, killed with Ctrl+C, and killed by signal N.
Regex
matches POSIX extended regex; capture groups land in .
re='^([0-9]+)\.([0-9]+)'
if [[ $version =~ $re ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
fi- Keep the pattern in a variable and unquoted after ; quoting it makes bash match the text literally.
- POSIX extended regex has no , or . Write , and instead.
- is overwritten by the next , so copy what you need straight away.
- statements and use globs (, , ), not regex.
- To match inside files use grep; to edit with a pattern use sed.
Functions
deploy() {
local target="$1"
if [[ -z "$target" ]]; then
echo "usage: deploy <host>" >&2
return 2
fi
rsync -az ./site/ "$target:/var/www/"
}
deploy web1- keeps a variable inside the function; without it, everything leaks into the script.
- only sets an exit code (0 to 255). To hand back data, it and capture with .
- Define a function before its first call; bash reads top to bottom.
Running Scripts
| Command | What it does |
|---|---|
| Run it with bash whatever the permissions say | |
| Run it directly; needs and a shebang line | |
| Run it in the current shell, so its variables and survive | |
| The shebang: first line of the file, finds bash on | |
| Check the syntax without running anything | |
| Trace every line as it runs ( to trace only part of one) | |
| Which bash you are actually on | |
| Replace the shell with cmd instead of starting a second process | |
| The script's own directory, whatever you ran it from |
"command not found" for a script sitting right in front of you is the missing . The current directory is not on , so bash never looks there.
Script Arguments
| Variable | What it holds |
|---|---|
| The script's own name | |
| ... | Positional arguments ( and up need braces) |
| How many arguments were passed | |
| All arguments, each kept as its own word; always quote it | |
| All arguments joined into one string; rarely what you want | |
| Drop and slide the rest down | |
| The script's PID | |
| PID of the last background job |
Flag parsing with the builtin:
while getopts "f:v" opt; do
case "$opt" in
f) file="$OPTARG" ;;
v) verbose=1 ;;
*) echo "usage: $0 [-v] [-f file]" >&2; exit 2 ;;
esac
done
shift $((OPTIND - 1)) # what remains is in "$@"Windows Equivalents
For moving between a Linux box and a Windows one. Windows PowerShell 5.1 aliases the short names (, , ); PowerShell 7 on Linux and macOS drops those aliases so the real commands still work.
| Bash | PowerShell | CMD |
|---|---|---|
Bash itself runs on Windows through WSL or Git Bash, which is usually easier than translating a script.
Gotchas
- is not an assignment; it runs a command called . No spaces around .
- An unquoted splits on spaces and expands globs. Quote every expansion: , , .
- is not bash on Debian and Ubuntu; is dash there, and , arrays, and all fail. Use a shebang and run with .
- macOS ships bash 3.2 from 2007, so , , and fail on a Mac. The macOS default shell is zsh now; a current bash comes from Homebrew.
- runs the loop in a subshell, so variables set inside it vanish afterwards. Feed the loop with instead.
- with an unset expands to . at the top turns that typo into an error instead of a disaster.