Bash / Zsh
Reference

A comprehensive command-line reference for Bash and Zsh, organized by topic with examples and compatibility notes.

Beginner Intermediate Advanced
01

Basics & Navigation

Beginner

Core commands for moving around the filesystem and getting help.

Print working directory pwd

Outputs the absolute path of the current working directory.

$ pwd
# Output: /home/user
pwd has two modes: logical (default, follows symlinks as-is) and physical (-P resolves all symlinks). Bash's built-in pwd and /usr/bin/pwd may differ in behavior.
List directory contents ls

Lists files and directories. Add flags for detailed output.

$ ls
$ ls -la          # long + hidden files
$ ls -lh          # human-readable sizes
$ ls -lt          # sorted by modification time
$ ls -R           # recursive
-l
Long format (permissions, owner, size, date)
-a
Show hidden files (starting with .)
-h
Human-readable file sizes (1K, 234M)
-t
Sort by modification time, newest first
-r
Reverse sort order
-S
Sort by file size
Change directory cd

Navigate the filesystem. Several shortcuts save keystrokes.

$ cd /var/log      # absolute path
$ cd Documents    # relative path
$ cd ..           # parent directory
$ cd ~            # home directory
$ cd -            # previous directory
cd - returns to the directory stored in $OLDPWD. This variable is set automatically by the shell on every cd command.
Clear terminal clear

Clears the terminal screen. The keyboard shortcut is faster.

$ clear
# Keyboard shortcut: Ctrl+L (same effect, faster)
Note: clear sends a terminal escape sequence. Scrollback history remains accessible — it's not deleted. Use reset to fully reinitialize the terminal.
Get help man / --help

Access documentation for any command.

$ man ls          # full manual page
$ ls --help       # brief help text (GNU tools)
$ man -k keyword  # search man pages (same as apropos)
$ info ls         # GNU info system (more detailed)
Not all commands support --help. POSIX-standard utilities often use -h. BSD tools (macOS) may have different flags than GNU/Linux counterparts.
Print to screen echo / printf

echo prints text; printf is more powerful with formatting.

$ echo "Hello World"
$ echo -n "No newline"    # suppress trailing newline
$ echo -e "Tab:\there"   # interpret escape sequences
$ printf "%s is %d\n" "Age" 30
echo behavior varies across shells and implementations. For portable scripts, printf is recommended by POSIX. The -e flag is not POSIX-standard for echo.
02

Files & Directories

Beginner

Creating, copying, moving, and deleting files and directories.

Create empty file / update timestamps touch

Creates an empty file if it doesn't exist, or updates its access/modification timestamps.

$ touch file.txt
$ touch -t 202501011200 file.txt  # set specific time
$ touch -r ref.txt file.txt     # use ref file's time
touch creates the file if it doesn't exist. If it does exist, it updates timestamps to the current time. Both access time (atime) and modification time (mtime) are updated by default.
Create directories mkdir

Creates one or more directories. -p creates parent directories as needed.

$ mkdir newdir
$ mkdir -p a/b/c         # create nested dirs
$ mkdir -m 755 secured  # set permissions on creation
Without -p, mkdir will fail if a parent directory doesn't exist. With -p, it silently succeeds even if the directory already exists.
Copy files and directories cp

Copies files or directories. Use -r for directories.

$ cp file.txt backup.txt
$ cp -r dir/ dir_copy/   # recursive copy
$ cp -p file.txt dest/   # preserve metadata
$ cp -u src dest/        # copy only if newer
-r / -R
Recursive (required for directories)
-p
Preserve timestamps, ownership, permissions
-u
Copy only when source is newer
-i
Prompt before overwrite
-n
No-clobber: never overwrite
Move / rename files mv

Moves or renames files and directories.

$ mv file.txt newname.txt
$ mv file.txt /tmp/
$ mv -i src dest       # prompt before overwrite
$ mv -n src dest       # do not overwrite
mv across filesystems copies the file then removes the original. Within the same filesystem, it simply updates the directory entry (atomic, fast).
Remove files and directories rm

Permanently removes files or directories. There is no recycle bin — use with care.

$ rm file.txt
$ rm -r directory/      # recursive removal
$ rm -f file.txt        # force, no error if missing
$ rm -i *.log           # interactive confirmation
Warning: rm -rf / or rm -rf /* will destroy your system. Modern GNU coreutils adds a --no-preserve-root guard, but do not rely on it.
Find files and directories find

Searches the filesystem recursively based on name, type, size, date, or other criteria.

$ find . -name "*.log"
$ find /var -type f -size +10M
$ find . -mtime -7              # modified in last 7 days
$ find . -name "*.sh" -exec chmod +x {} \;
The -exec action runs a command for each result. The {} is replaced with the filename. The \; terminates the command. Using + instead of \; batches filenames for efficiency.
View file content cat / less / head / tail

Multiple tools for reading files — choose based on file size and need.

$ cat file.txt               # print entire file
$ less file.txt              # paginated viewer (q to quit)
$ head -n 20 file.txt        # first 20 lines
$ tail -n 20 file.txt        # last 20 lines
$ tail -f /var/log/syslog    # follow new output live
cat concatenates files — the name comes from "concatenate." For large files, use less instead. tail -f follows the file as it grows, useful for log monitoring.
Disk usage and space du / df

Check how much disk space is used.

$ df -h                    # filesystem disk space
$ du -sh /var/log          # size of a directory
$ du -sh * | sort -h       # all items, sorted
df reports on mounted filesystems. du walks the directory tree and sums file sizes. -h (human-readable) is available in GNU and BSD versions. The sort -h flag sorts by human-readable size (GNU coreutils).
03

Permissions

Intermediate

Unix file permissions use a three-set model: owner, group, and others.

Change file permissions chmod

Sets read/write/execute permissions using octal notation or symbolic mode.

$ chmod 755 script.sh    # rwxr-xr-x
$ chmod 644 file.txt     # rw-r--r--
$ chmod +x script.sh     # add execute for all
$ chmod u+x,g-w file     # symbolic: user+exec, group-write
$ chmod -R 755 dir/      # recursive
Octal digits: 4=read, 2=write, 1=execute. Three digits apply to: owner, group, others. 755 = rwxr-xr-x, 644 = rw-r--r--. The execute bit on a directory means "traverse" (enter), not execute a program.
Change file ownership chown

Changes the owner and/or group of a file or directory.

$ chown user file.txt
$ chown user:group file.txt
$ chown :group file.txt      # change group only
$ chown -R user:group dir/   # recursive
Regular users can only change the group to one they belong to. Only root can change ownership to another user. On macOS/BSD, chown user.group uses a dot instead of colon (both work on Linux).
Change group ownership chgrp

Changes the group of a file or directory (shorthand for chown :group).

$ chgrp developers project/
$ chgrp -R staff /srv/files
Understanding permission bits ls -l

Read the output of ls -l to understand permission strings.

-rwxr-xr-x  1  user  group  4096  Jan 1  file.txt
│└──┴──┴──  │  │     │      │     │      │
│  u  g  o  │  owner group size  date   name
└ type      └ links
The first character is the file type: -=regular, d=directory, l=symlink, c=char device, b=block device, p=named pipe, s=socket.
04

Process Management

Intermediate

Viewing, controlling, and signaling running processes.

List running processes ps

Snapshot of current processes. Commonly used with aux flags.

$ ps aux               # all processes, all users
$ ps -ef               # full-format listing (POSIX)
$ ps aux | grep nginx  # find specific process
ps aux is BSD-style syntax (no dash). ps -ef is POSIX/SysV-style (with dash). Both work on Linux. The a flag shows all users' processes, u adds user-oriented format, x includes processes without a terminal.
Interactive process viewer top / htop

Real-time view of running processes, sorted by CPU/memory.

$ top               # built-in, press q to quit
$ htop              # enhanced viewer (may need install)
$ top -u username   # filter by user
Tip: In top, press P to sort by CPU, M to sort by memory, k to kill a process. htop supports mouse interaction and is far more user-friendly.
Kill a process kill / killall / pkill

Sends signals to processes. The default signal is SIGTERM (15), which allows cleanup.

$ kill 1234          # SIGTERM to PID 1234
$ kill -9 1234       # SIGKILL (force, no cleanup)
$ killall nginx      # kill by process name
$ pkill -f "python"  # kill by pattern matching full cmd
SIGKILL (9) cannot be caught or ignored by the process — the kernel terminates it immediately. SIGTERM (15) is the polite request that allows graceful shutdown. Always try SIGTERM before SIGKILL.
Job control & / bg / fg / jobs

Run commands in the background and switch between foreground/background.

$ sleep 60 &         # run in background
$ jobs               # list background jobs
$ fg %1              # bring job 1 to foreground
$ bg %1              # resume stopped job in background
# Ctrl+Z  to suspend a foreground job
Background jobs (&) are tied to the shell session and will be terminated when the session ends, unless started with nohup or disown. Use disown %1 to detach a job from the shell.
Persistent processes nohup / disown

Keep processes running after the terminal session closes.

$ nohup command &           # immune to SIGHUP
$ nohup command > out.log &  # redirect output
$ command & disown          # disown from current shell
nohup redirects stdout/stderr to nohup.out by default if not redirected. disown removes the job from the shell's job table so it won't receive SIGHUP when the shell exits.
Resource limits ulimit

View or set per-process resource limits for the current shell session.

$ ulimit -a          # show all limits
$ ulimit -n 4096     # max open file descriptors
$ ulimit -s          # show stack size limit
ulimit is a shell built-in, not an external command. Limits apply to the current shell and all child processes. Hard limits can only be raised by root; soft limits can be raised up to the hard limit by any user.
05

Text Processing

Intermediate

Filter, search, transform, and analyze text streams and files.

Search text patterns grep

Searches for lines matching a pattern. One of the most used Unix tools.

$ grep "error" log.txt
$ grep -r "TODO" ./src/      # recursive
$ grep -i "warning" log.txt  # case-insensitive
$ grep -n "error" log.txt   # show line numbers
$ grep -v "debug" log.txt   # invert (exclude)
$ grep -E "err|warn" log    # extended regex (egrep)
$ grep -c "404" access.log  # count matches
-r / -R
Recursive search in directories
-i
Case-insensitive matching
-v
Invert match (show non-matching lines)
-n
Show line numbers
-l
Show only filenames with matches
-c
Count matching lines
-A 2
Show 2 lines after match
-B 2
Show 2 lines before match
-E
Extended regex (same as egrep)
-P
Perl-compatible regex (GNU grep)
Stream editor sed

Edit text streams via substitution, deletion, and insertion rules.

$ sed 's/foo/bar/' file.txt      # first occurrence per line
$ sed 's/foo/bar/g' file.txt     # global (all occurrences)
$ sed -i 's/foo/bar/g' file.txt  # in-place edit
$ sed '2,5d' file.txt            # delete lines 2–5
$ sed -n '10,20p' file.txt       # print only lines 10–20
On macOS/BSD, sed -i requires an explicit backup extension: sed -i '' 's/foo/bar/g' file. On GNU/Linux, sed -i works without an extension. This is a common portability pitfall.
Text processing language awk

A powerful pattern-action language for column-oriented text processing.

$ awk '{print $1}' file.txt       # print first field
$ awk -F: '{print $1}' /etc/passwd # colon delimiter
$ awk 'NR==5' file.txt            # print 5th line
$ awk '$3 > 100' data.txt         # filter by field value
$ awk '{sum+=$1} END{print sum}' f # sum a column
awk field separator defaults to whitespace (any combination of spaces/tabs). $0 is the entire line, $1 is the first field, NR is the current line number, NF is the number of fields on the current line.
Sort and deduplicate sort / uniq

sort reorders lines; uniq removes consecutive duplicates (sort first).

$ sort file.txt
$ sort -r file.txt       # reverse order
$ sort -n numbers.txt    # numeric sort
$ sort -k 2 file.txt     # sort by second field
$ sort file.txt | uniq   # remove duplicates
$ sort file.txt | uniq -c # count occurrences
uniq only removes adjacent duplicate lines. Always sort first to remove all duplicates. sort -u is equivalent to sort | uniq and often faster.
Cut and paste fields cut / paste

Extract columns from structured text or merge files side by side.

$ cut -d: -f1 /etc/passwd    # usernames (field 1)
$ cut -d, -f2,4 file.csv   # fields 2 and 4
$ cut -c1-10 file.txt      # characters 1–10
$ paste file1.txt file2.txt # merge side by side
Word / line count wc

Counts lines, words, and characters in files or stdin.

$ wc -l file.txt      # count lines
$ wc -w file.txt      # count words
$ wc -c file.txt      # count bytes
$ wc -m file.txt      # count characters (Unicode-aware)
-c counts bytes, not characters. For multi-byte characters (UTF-8), use -m to count characters. On some systems, -m and -c give the same result for ASCII-only files.
06

I/O Redirection & Pipes

Intermediate

Direct the flow of data between commands, files, and file descriptors.

Standard streams stdin / stdout / stderr

Every process has three standard file descriptors: stdin (0), stdout (1), stderr (2).

# File descriptors:
# 0 = stdin   (keyboard by default)
# 1 = stdout  (terminal by default)
# 2 = stderr  (terminal by default)
$ command > out.txt        # redirect stdout
$ command >> out.txt       # append stdout
$ command 2> err.txt       # redirect stderr
$ command > out.txt 2>&1   # both to same file
$ command &> out.txt       # Bash shorthand for above
&> and >& are Bash-specific shorthand. In POSIX sh, use command > file 2>&1. Order matters: 2>&1 > file is incorrect — stderr would still go to the terminal.
Pipes |

Connects stdout of one command to stdin of the next. The foundation of Unix philosophy.

$ ls -la | less
$ ps aux | grep nginx | awk '{print $2}'
$ cat access.log | cut -d' ' -f1 | sort | uniq -c | sort -rn
Pipe stages run in subshells. Variable assignments inside a pipe are not visible in the parent shell. This is a common source of bugs in scripts. In Bash 4.2+, the lastpipe option changes this behavior.
Here documents <<

Pass multiline input to a command without a file.

$ cat << EOF
  Line one
  Line two
EOF

$ ssh user@host << 'ENDSSH'
  echo "On remote machine"
ENDSSH
Quoting the delimiter (<< 'EOF') prevents variable expansion and command substitution inside the heredoc. Without quotes, $var and $(cmd) are expanded.
Here strings <<<

Pass a single string as stdin to a command. Bash/Zsh specific.

$ grep "foo" <<< "foobar"
$ read -r first last <<< "John Doe"
$ base64 <<< "hello"
Here strings (<<<) are a Bash/Zsh extension, not POSIX. The string is opened as a temporary file and passed as stdin. A newline is appended automatically.
Process substitution <() and >()

Treat the output of a command as if it were a file. Bash/Zsh specific.

$ diff <(sort file1.txt) <(sort file2.txt)
$ tee >(gzip > output.gz) < input.txt
Process substitution uses named pipes or /dev/fd/N under the hood. It is not available in POSIX sh or dash. Useful when a command requires a filename argument but you want to use command output.
Discard output /dev/null

Redirect to /dev/null to silently discard output.

$ command > /dev/null         # discard stdout
$ command 2> /dev/null        # discard stderr
$ command > /dev/null 2>&1    # discard all output
Tip: /dev/null is a special file that discards all data written to it. Reading from it returns EOF immediately. It's sometimes called the "bit bucket."
07

Scripting

Intermediate

Control flow, functions, and error handling for shell scripts.

Shebang / script setup #!/bin/bash

The first line of a script tells the OS which interpreter to use.

#!/bin/bash
# Strict mode — catch errors early
set -euo pipefail
IFS=$'\n\t'
#!/usr/bin/env bash is more portable than #!/bin/bash as it searches $PATH for bash. set -e exits on error, -u treats unset variables as errors, -o pipefail catches pipe failures.
Conditionals if / elif / else

Test conditions and branch execution. Use [[ ]] in Bash/Zsh (safer than [ ]).

if [[ -f "$file" ]]; then
  echo "File exists"
elif [[ -d "$file" ]]; then
  echo "It's a directory"
else
  echo "Not found"
fi

# Test flags: -f file, -d dir, -z empty, -n non-empty
# -e exists, -r readable, -w writable, -x executable
[[ ]] is a Bash/Zsh keyword (not a command). It supports pattern matching (==), regex (=~), and logical operators (&&, ||) without quoting issues that affect [ ].
Loops for / while / until

Iterate over items or repeat while a condition holds.

# C-style for loop
for ((i=0; i<5; i++)); do echo "$i"; done

# Iterate over array
for item in "${array[@]}"; do echo "$item"; done

# While loop
while [[ $count -lt 10 ]]; do
  ((count++))
done

# Read lines from a file
while IFS= read -r line; do
  echo "$line"
done < file.txt
When reading files line by line, IFS= read -r line is the correct idiom: IFS= prevents stripping leading/trailing whitespace, -r prevents backslash interpretation.
Functions function / ()

Define reusable code blocks. Arguments accessed via $1, $2, etc.

# POSIX-compatible syntax
greet() {
  local name="$1"
  echo "Hello, $name"
  return 0
}

# Bash-specific syntax (equivalent)
function greet { ... }

greet "World"
Use local to limit variable scope to the function. Without local, variables are global to the script. Functions return exit codes (0–255) via return, not values. Use echo + command substitution to "return" strings.
Exit codes and error handling $? / trap

Every command returns an exit code. 0 = success, non-zero = error.

$ command; echo $?        # print exit code
command || { echo "Failed"; exit 1; }
command && echo "Success"

# Cleanup on exit
cleanup() { rm -f /tmp/myfile; }
trap cleanup EXIT
trap 'echo "Ctrl+C pressed"' INT
trap registers signal handlers. Common signals: EXIT (any exit), INT (Ctrl+C), TERM (kill), ERR (with set -e). trap - SIGNAL resets a signal to its default behavior.
Arrays () / []

Bash arrays store ordered lists of values. Zsh arrays are 1-indexed by default.

# Bash arrays (0-indexed)
arr=(one two three)
echo "${arr[0]}"           # first element
echo "${arr[@]}"           # all elements
echo "${#arr[@]}"          # array length
arr+=(four)               # append element
unset arr[1]              # remove element
In Zsh, arrays are 1-indexed by default. In Bash, arrays are 0-indexed. Setting setopt KSH_ARRAYS in Zsh makes them 0-indexed for compatibility.
08

Variables & Expansion

Intermediate

Variable assignment, parameter expansion, and command substitution.

Variable basics $var / ${var}

Assign and read variables. No spaces around =.

# Assignment (no spaces around =)
name="Alice"
age=30
path="/home/$name"

# Reading
echo "$name"
echo "${name}s"     # braces prevent ambiguity
echo "${#name}"     # string length: 5
Always quote variable expansions ("$var"). Unquoted $var undergoes word splitting and glob expansion, which causes bugs when values contain spaces or special characters.
Parameter expansion ${var:-default}

Bash/Zsh provide powerful inline variable manipulation without external tools.

${var:-default}    # use default if unset/empty
${var:=default}    # set and use default if unset/empty
${var:+value}      # use value only if var is set
${var:?error msg}  # exit with error if unset/empty
${var:0:5}         # substring: offset 0, length 5
${var#prefix}      # remove shortest prefix match
${var##prefix}     # remove longest prefix match
${var%suffix}      # remove shortest suffix match
${var%%suffix}     # remove longest suffix match
${var/pat/repl}    # replace first match
${var//pat/repl}   # replace all matches
These expansions are POSIX-standard (except // global replace, which is Bash/Zsh). They are faster than spawning sed or awk for simple string operations.
Command substitution $(cmd)

Capture the output of a command as a string value.

today=$(date +%Y-%m-%d)
files=$(ls -la | wc -l)
echo "Today is $today"
echo "Files: $files"

# Old backtick syntax (avoid — harder to nest)
today=`date`
The modern $() syntax is preferred over backticks. It nests cleanly: $(echo $(date)). Backtick nesting requires escaping: `echo \`date\``.
Arithmetic expansion $((...))

Perform integer arithmetic directly in the shell.

echo $((2 + 3))        # 5
echo $((10 % 3))       # 1 (modulo)
echo $((2 ** 8))       # 256 (exponentiation)
((count++))            # increment without echo
result=$(( (a + b) * c ))

# For floating point, use bc or awk:
echo "scale=2; 10/3" | bc     # 3.33
Shell arithmetic only handles integers. $((10/3)) returns 3 (truncated). For floating point, use bc, awk, or python3 -c "print(10/3)".
Environment variables export / env

Environment variables are passed to child processes; shell variables are not.

$ export MY_VAR="value"      # make available to children
$ env                        # list all environment variables
$ printenv HOME              # print one variable
$ unset MY_VAR               # remove variable
$ MY_VAR=val command         # set for one command only
Shell variables exist only in the current shell. export marks them for inheritance by child processes. A child process cannot modify the parent shell's environment.
09

Networking

Intermediate

Network diagnostics, file transfer, and remote access tools.

Test connectivity ping / traceroute

Test host reachability and trace the network path.

$ ping -c 4 google.com
$ ping -c 4 -i 0.2 host   # faster interval
$ traceroute google.com   # Linux
$ tracert google.com      # Windows equivalent
ping uses ICMP ECHO_REQUEST packets. Some hosts block ICMP, so no response doesn't necessarily mean the host is down. On macOS, use traceroute; on Linux, traceroute or mtr for combined ping+trace.
HTTP requests curl

Transfer data using URLs. Supports HTTP, HTTPS, FTP, and more.

$ curl https://example.com
$ curl -o file.txt https://example.com/file
$ curl -L url              # follow redirects
$ curl -I https://example.com  # headers only
$ curl -X POST -d '{"k":"v"}' -H "Content-Type: application/json" url
-o
Save to file
-O
Save using remote filename
-L
Follow redirects
-I
Fetch headers only
-s
Silent mode
-X POST
Set HTTP method
-d
Request body
-H
Add header
SSH remote access ssh

Securely connect to remote machines and execute commands.

$ ssh user@host
$ ssh -p 2222 user@host       # custom port
$ ssh -i ~/.ssh/key.pem user@host
$ ssh user@host "ls -la"     # run remote command
$ ssh -L 8080:localhost:80 user@host  # local port forward
SSH default port is 22. Key-based authentication is strongly recommended over passwords. The ~/.ssh/config file can store connection profiles to avoid typing long commands repeatedly.
Secure file transfer scp / rsync

Copy files to/from remote hosts over SSH.

$ scp file.txt user@host:/path/
$ scp user@host:/path/file.txt .
$ scp -r dir/ user@host:/path/

# rsync — efficient incremental sync
$ rsync -avz src/ user@host:/dest/
$ rsync -avz --delete src/ dest/
scp is being deprecated in favor of sftp and rsync in OpenSSH. rsync uses delta transfer — only changed portions of files are sent, making it far more efficient for large syncs.
Network diagnostics netstat / ss / nmap

Inspect network connections, open ports, and routing.

$ ss -tulnp              # listening ports (modern)
$ netstat -tulnp          # same (older systems)
$ nmap -sV localhost      # scan open ports + services
$ ip addr show           # network interfaces
$ ip route show          # routing table
netstat is deprecated on modern Linux in favor of ss (from the iproute2 package). ifconfig is similarly replaced by ip. ss reads directly from kernel structures and is faster.
10

Archives & Compression

Beginner

Create, extract, and inspect compressed archives.

tar archives tar

The standard Unix tool for creating and extracting archives.

# Create
$ tar -czf archive.tar.gz dir/    # gzip compressed
$ tar -cjf archive.tar.bz2 dir/   # bzip2 compressed
$ tar -cJf archive.tar.xz dir/    # xz compressed

# Extract
$ tar -xzf archive.tar.gz
$ tar -xzf archive.tar.gz -C /path/ # to directory

# List contents
$ tar -tzf archive.tar.gz
-c
Create archive
-x
Extract archive
-t
List contents
-z
Filter through gzip
-j
Filter through bzip2
-J
Filter through xz
-f
Archive filename
-v
Verbose output
-C dir
Extract to directory
Zip archives zip / unzip

Create and extract ZIP archives, compatible with Windows.

$ zip archive.zip file1.txt file2.txt
$ zip -r archive.zip directory/
$ unzip archive.zip
$ unzip -l archive.zip          # list contents
$ unzip archive.zip -d /path/   # extract to directory
ZIP uses its own compression and directory structure. tar with gzip (.tar.gz) generally achieves better compression for multiple files because it compresses the combined stream, not each file individually.
gzip / bzip2 / xz single file compression

Compress or decompress individual files (not archives).

$ gzip file.txt           # creates file.txt.gz, removes original
$ gzip -k file.txt        # keep original
$ gzip -d file.txt.gz     # decompress (same as gunzip)
$ bzip2 file.txt          # better ratio, slower
$ xz file.txt             # best ratio, slowest
Compression trade-offs: gzip (fastest, moderate compression), bzip2 (slower, better compression), xz (slowest, best compression). For most cases, tar -czf (gzip) is the practical choice.
11

Zsh-Specific Features

Intermediate

Zsh extends Bash with powerful autocompletion, globbing, and interactive features.

Extended globbing **/ and qualifiers

Zsh glob patterns are far more powerful than Bash's.

# Recursive glob (default in zsh, needs globstar in bash)
ls **/*.txt         # all .txt files recursively
ls **/*(.)          # regular files only
ls **/*(/)          # directories only
ls **/*(Lk+100)     # files over 100KB
ls **/*(mh-1)       # modified in last hour
In Bash, ** requires shopt -s globstar. Zsh's ** is enabled by default. Zsh glob qualifiers in parentheses are a Zsh-only feature not available in Bash.
Spelling correction setopt CORRECT

Zsh can suggest corrections for mistyped commands.

setopt CORRECT        # suggest corrections for commands
setopt CORRECT_ALL    # suggest for all words

# In ~/.zshrc for permanent setting
Tip: When Zsh suggests a correction, you can accept it with y, reject it with n, or type the correct command manually with e.
Directory navigation AUTO_CD / CDPATH

Type directory names without cd, and navigate history.

setopt AUTO_CD        # type dir name to cd
setopt AUTO_PUSHD     # push dirs onto stack
setopt PUSHD_IGNORE_DUPS

$ dirs -v            # view directory stack
$ ~2                  # cd to stack entry 2
AUTO_CD is one of Zsh's most-used features. It checks if the typed word is a valid directory and runs cd automatically. This does not work in Bash without an alias or function.
Oh My Zsh plugin/theme framework

A popular framework for managing Zsh configuration, themes, and plugins.

# Install Oh My Zsh
$ sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

# ~/.zshrc configuration
ZSH_THEME="robbyrussell"
plugins=(git docker kubectl zsh-autosuggestions)
The official Oh My Zsh install URL uses the GitHub raw domain. Always verify install scripts before running them. Alternatives include Prezto, Zinit, and Antigen for lighter-weight frameworks.
Zsh completion system compinit / zstyle

Zsh's completion system (compsys) is the most powerful of any shell.

# Enable completion in ~/.zshrc
autoload -Uz compinit
compinit

# Styled menus
zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'
compinit loads and initializes the completion system. Running it without -C rebuilds the completion cache on every shell start. Use compinit -C to skip the security check and rebuild only when needed.
12

Git Essentials

Beginner

Version control fundamentals for everyday development.

Repository setup git init / clone

Initialize a new repo or clone an existing one.

$ git init                    # new repo in current dir
$ git init project-name       # new repo in new dir
$ git clone https://github.com/user/repo.git
$ git clone --depth 1 url     # shallow clone (no history)
Staging and committing add / commit

Stage changes and record them in the repository history.

$ git add file.txt            # stage specific file
$ git add .                   # stage all changes
$ git add -p                  # stage interactively (hunks)
$ git commit -m "message"
$ git commit --amend          # modify last commit
git add . stages all changes in the current directory and subdirectories. git add -A stages all changes including deletions from anywhere in the working tree.
Branches branch / checkout / switch

Create, switch, and manage branches.

$ git branch                  # list branches
$ git branch feature/login    # create branch
$ git switch feature/login    # switch (Git 2.23+)
$ git switch -c new-branch    # create and switch
$ git checkout -b new-branch  # older equivalent
git switch and git restore were introduced in Git 2.23 (August 2019) to split the overloaded git checkout command. switch handles branch changes; restore handles file restoration.
Remote operations fetch / pull / push

Sync with remote repositories.

$ git fetch origin
$ git pull origin main
$ git pull --rebase origin main
$ git push origin feature/login
$ git push -u origin main     # set upstream tracking
Tip: git pull --rebase replays your local commits on top of the fetched upstream, creating a cleaner linear history compared to a merge commit.
History inspection log / diff / blame

Explore commit history and file changes.

$ git log --oneline --graph
$ git log -p file.txt         # patches for a file
$ git diff HEAD~1             # changes since last commit
$ git diff main..feature      # between branches
$ git blame file.txt          # who changed each line
Undoing changes revert / reset / restore

Different tools for different undo scenarios.

# Undo a commit (safe — adds new commit)
$ git revert HEAD

# Unstage a file
$ git restore --staged file.txt

# Reset to a commit (destructive)
$ git reset --soft HEAD~1   # keep changes staged
$ git reset --hard HEAD~1   # discard all changes
Warning: git reset --hard permanently discards uncommitted changes. git revert is safer for shared branches because it doesn't rewrite history.
13

Advanced Patterns

Advanced

Power-user techniques for efficient scripting and shell usage.

Brace expansion {a,b,c}

Generate multiple strings from a pattern. Expanded before any other processing.

$ echo {a,b,c}.txt     # a.txt b.txt c.txt
$ echo {1..5}          # 1 2 3 4 5
$ echo {01..10}        # 01 02 03 ... 10 (zero-padded)
$ mkdir -p project/{src,tests,docs}
$ cp file.txt{,.bak}   # copy file.txt to file.txt.bak
Brace expansion is a shell feature, not a glob. It runs before filename expansion and doesn't require files to exist. Zero-padding in sequences ({01..10}) is a Bash 4.0+ feature.
xargs xargs

Build and execute commands from standard input — bridge between pipes and commands that don't read stdin.

$ find . -name "*.log" | xargs rm
$ cat urls.txt | xargs -n 1 curl -O
$ find . -name "*.js" | xargs grep -l "TODO"
$ echo "a b c" | xargs -n 1 echo
# Parallel execution:
$ find . -name "*.png" | xargs -P 4 -I{} convert {} {}.webp
xargs passes arguments as a batch by default (up to ARG_MAX). Use -n 1 to process one argument at a time. Use -P N for parallel execution with N processes. Use -0 with find -print0 to handle filenames with spaces.
tee tee

Write to both stdout and a file simultaneously — like a pipe T-junction.

$ command | tee output.txt
$ command | tee -a output.txt    # append
$ command | tee file1 file2       # multiple files
$ command | tee >(process1) >(process2)
tee reads from stdin and writes to both stdout and files. It's useful for logging while still seeing output. The name comes from the T-shaped pipe fitting in plumbing.
String operations without subshells parameter expansion

Efficient string manipulation without spawning external processes.

# Extract filename from path
path="/home/user/docs/file.txt"
echo "${path##*/}"       # file.txt
echo "${path%/*}"        # /home/user/docs
echo "${path%.txt}"      # strip extension

# Case conversion (Bash 4+)
str="Hello World"
echo "${str,,}"          # hello world
echo "${str^^}"          # HELLO WORLD
Case conversion with ${var,,} and ${var^^} requires Bash 4.0+. macOS ships with Bash 3.x by default. Install Bash via Homebrew (brew install bash) or use Zsh, which also supports these modifiers.
Named pipes (FIFOs) mkfifo

Create persistent named pipes for inter-process communication.

$ mkfifo mypipe
# Terminal 1:
$ command1 > mypipe
# Terminal 2:
$ command2 < mypipe
$ rm mypipe              # clean up
Use case: Named pipes are useful when process substitution isn't available, or when you need to communicate between processes in separate terminal sessions.
Conditional execution && and ||

Run commands conditionally based on the exit code of the previous command.

# Run second only if first succeeds
mkdir project && cd project

# Run second only if first fails
ping -c1 host || echo "Host unreachable"

# Chain with grouping
{ command1 && command2; } || fallback
&& uses short-circuit evaluation — the right side only runs if the left side exits 0. || runs the right side only if the left side exits non-zero. These are not boolean operators but flow control based on exit codes.
14

Keyboard Shortcuts

Beginner

Readline shortcuts work in Bash and Zsh (Emacs mode by default).

Cursor movement Ctrl + keys

Move the cursor without using arrow keys.

Ctrl+A    # beginning of line
Ctrl+E    # end of line
Ctrl+F    # forward one character
Ctrl+B    # backward one character
Alt+F     # forward one word
Alt+B     # backward one word
Ctrl+XX   # toggle between start and cursor position
These shortcuts use GNU Readline (Emacs mode). Switch to Vi mode with set -o vi (Bash) or bindkey -v (Zsh). In Vi mode, Esc enters command mode with different keybindings.
Editing shortcuts Ctrl + keys

Cut, copy, paste, and modify text on the command line.

Ctrl+K    # cut from cursor to end of line
Ctrl+U    # cut from start of line to cursor
Ctrl+W    # cut previous word
Ctrl+Y    # paste (yank) last cut text
Alt+D     # cut next word
Ctrl+T    # swap current and previous characters
Alt+T     # swap current and previous words
Ctrl+_    # undo
History navigation Ctrl + keys

Search and navigate command history efficiently.

Ctrl+R    # reverse incremental search
Ctrl+S    # forward search (may be blocked by terminal)
Ctrl+P    # previous command (same as ↑)
Ctrl+N    # next command (same as ↓)
!!        # repeat last command
!$        # last argument of previous command
!*        # all arguments of previous command
Ctrl+S may be blocked by XON/XOFF flow control. Run stty -ixon (or add it to ~/.bashrc) to enable forward history search. In Zsh, Ctrl+S works by default.
Process control Ctrl + keys

Control the terminal and running processes.

Ctrl+C    # send SIGINT (interrupt/kill foreground)
Ctrl+Z    # send SIGTSTP (suspend foreground job)
Ctrl+D    # send EOF / logout from shell
Ctrl+L    # clear screen (same as clear)
Ctrl+\\   # send SIGQUIT (quit with core dump)
Ctrl+C sends SIGINT (signal 2), not SIGTERM or SIGKILL. Programs can catch and handle SIGINT. Ctrl+Z sends SIGTSTP which suspends (not kills) the process — it can be resumed with fg.
15

Tips & Tricks

Intermediate

Practical techniques that improve daily command-line productivity.

History tricks history / !

The shell history is a powerful tool — learn to exploit it.

$ history               # list command history
$ history | grep ssh     # search history
$ !42                    # run command #42
$ !ssh                   # run last command starting with ssh
$ !!:s/old/new           # run last cmd with substitution
# HISTSIZE=10000 in ~/.bashrc/.zshrc
Tmux / screen terminal multiplexer

Run multiple terminal sessions within one window, and keep sessions alive when disconnected.

$ tmux                   # start new session
$ tmux new -s work        # named session
$ tmux attach -t work     # re-attach to session
Ctrl+B D              # detach (session lives on)
Ctrl+B C              # create new window
Ctrl+B %              # vertical split pane
Tmux prefix key is Ctrl+B by default. screen uses Ctrl+A. Both tools persist processes when your SSH connection drops. Tmux is generally preferred for new setups.
Aliases alias

Create shortcuts for frequently used commands.

# In ~/.bashrc or ~/.zshrc:
alias ll="ls -la"
alias la="ls -A"
alias ..="cd .."
alias ...="cd ../.."
alias gs="git status"
alias python="python3"

$ alias          # list all aliases
$ unalias ll     # remove alias
Aliases are only available in interactive shells by default. To use them in scripts, run shopt -s expand_aliases (Bash) before calling them. Functions are more versatile than aliases for complex operations.
Useful one-liners combinations

Commonly useful shell one-liners for everyday tasks.

# Find and kill a port's process
$ lsof -ti:3000 | xargs kill

# Create a dated backup
$ cp file.txt "file.$(date +%Y%m%d).bak"

# Repeat a command every 2 seconds
$ watch -n 2 df -h

# Show largest files in current dir
$ du -sh * | sort -rh | head -10
16

Special Variables

Intermediate

Built-in variables that the shell automatically sets and maintains.

Positional parameters $0 — $@

Automatic variables set when a script is run or a function is called.

$0        # name of the script / shell
$1, $2    # first, second argument
$@        # all arguments (array — preserves quoting)
$*        # all arguments (as single string)
$#        # number of arguments
"$@" and "$*" differ when quoted: "$@" expands to separate quoted words (correct for passing arguments), "$*" expands to one single string. Always use "$@" for passing arguments to functions/commands.
Process and status variables $$ — $?

Variables about the current shell process and command status.

$$        # current shell's PID
$!        # PID of last background process
$?        # exit status of last command (0=success)
$-        # current shell option flags
$_        # last argument of previous command
$$ gives the PID of the shell running the script (not a subshell). In subshells ($(...)), use $BASHPID (Bash 4+) to get the actual current PID. $$ always refers to the parent shell.
Shell environment variables $HOME — $PATH

Standard environment variables set by the system or login shell.

$HOME          # current user's home directory
$USER          # current username
$PATH          # colon-separated executable search path
$SHELL         # path to current shell binary
$PWD           # current working directory
$OLDPWD        # previous working directory
$EDITOR        # preferred text editor
$HISTFILE      # path to history file
$LANG          # locale setting
Bash/Zsh specific variables $BASH_VERSION

Variables specific to Bash or Zsh that expose shell internals.

# Bash
$BASH_VERSION   # e.g., "5.1.16(1)-release"
$BASH_SOURCE    # array of source file paths
$LINENO         # current line number in script
$FUNCNAME       # array of function call stack

# Zsh
$ZSH_VERSION    # e.g., "5.9"
$PROMPT         # prompt string
$fpath          # function search path
$BASH_SOURCE[0] gives the path of the currently executing script, even when sourced. This is useful for scripts that need to find their own location: DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)".