Cheat Sheet

Bash Cheat Sheet

Bash is the default shell on most Linux servers, so half of any bash cheat sheet is just Linux commands. This sheet covers both sides: the commands you type at the prompt and the syntax that only matters in scripts, from test operators to arrays and parameter expansion.

Last updated September 11, 2026

CommandWhat 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

CommandWhat 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.

CommandWhat 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

CommandWhat 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.

CommandWhat 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
TestTrue 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
done

leaves 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]}"
done

Parameter 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.

ExpansionWhat 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.

CommandWhat 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.

PatternMatches
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

CommandWhat 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)
EOF

Command Substitution

CommandWhat 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.

CommandWhat 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 0

Prompting 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.

KeyAction
/ 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.

KeyAction
/ 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

CommandWhat 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 command

Startup Files

FileWhen 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

CommandWhat 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

CommandWhat 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

VariableWhat 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.

BashPowerShellCMD

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.

Bash FAQ

Are bash commands the same as Linux commands?

Mostly, which is why the two cheat sheets blur together. Commands like ls, grep, and ssh are separate programs that bash merely runs, and they work the same from zsh or any other shell. What actually belongs to bash is the syntax around them: variables, [[ ]] tests, loops, redirection, and builtins like cd, export, and alias. Type ls into any Linux shell and it works; write a script full of [[ ]] and arrays and it only runs under bash.

What is the difference between sh and bash?

sh is the POSIX shell specification; bash is one implementation of it plus a lot of extras: [[ ]], arrays, ${var//old/new} expansions, brace ranges like {1..10}, and =~ regex matching. On Debian and Ubuntu, /bin/sh points at dash, a minimal shell, so running sh script.sh or using #!/bin/sh strips the bash extras and you get errors like "[[: not found". If a script uses anything from this page beyond plain commands and pipes, start it with #!/usr/bin/env bash and run it with bash.

How do I check if a file exists in bash?

[[ -f /path/file ]] is true for a regular file, [[ -d /path ]] for a directory, and [[ -e /path ]] for anything at that path. Negate with an exclamation mark: [[ ! -f config ]] && cp default config. Two traps: quote the variable, [[ -f "$file" ]], so paths with spaces work, and remember -f follows symlinks, so a link pointing at a deleted file tests false. In a script that must also run under plain sh, use [ -f file ] or test -f file instead.

Can a bash function return a value?

Not the way other languages do. return only sets an exit code, a number from 0 to 255, and anything larger wraps around, so return 300 comes back as 44. To hand back data, echo it and capture the call: out=$(myfunc). That runs the function in a subshell, so any variable it sets is gone afterwards. When you need both a value and side effects, assign to a variable the caller already knows about, or take the name as an argument and use declare -n ref="$1" to write through it, which is a bash 4.3 feature. Checking a function with if myfunc; then uses its exit code, not its output.

Should bash variables be uppercase?

Bash does not care, but the convention exists for a good reason. Uppercase is for environment variables and things you export, since that is the namespace PATH, HOME, EDITOR and every other system variable lives in. Lowercase is for variables local to your script, which keeps you from quietly overwriting something the system needs. Naming a loop counter PATH really will break the rest of the script. Bash also has read-only names such as UID, BASH_VERSION and RANDOM that fail to assign. Only letters, digits and underscores are allowed, and a name cannot start with a digit.

Related cheat sheets