Linux Commands Cheat Sheet — 200+ Commands by Task

Linux commands by task, from files and permissions to networking, systemd and text processing. Every entry has flags, copyable examples and macOS notes.

Linux Commands Cheat Sheet · freetoolsforweb.com/tools/linux-commands-cheat-sheet · verified 2026-10-18

Last verified: 2026-10-18 against GNU coreutils 9.x, util-linux, procps-ng, iproute2, systemd and bash 5.2 manuals; macOS notes against the macOS 26 man pages.

215 entries in 15 sections

Viewing & editing text

Read files, page through logs, edit in the terminal and compare versions.

cat

Print or join files

Linux · macOS#
cat [-n] [-A] file...
-n
number all lines
-A
show tabs as ^I and line ends as $ (GNU); reveals CRLF line endings
cat /etc/os-release

print a file

cat part1.txt part2.txt > whole.txt

concatenate

cat -A script.sh | head

spot hidden tabs and Windows ^M line endings

macOS/BSD: macOS cat has no -A; use cat -vet for the same view.

tac

Print a file with the last line first

Linux#
tac file
tac access.log | head

newest log lines first

macOS/BSD: Not on macOS; use tail -r file.

less

Page through a long file or output

Linux · macOS#
less [-N] [-S] [+F] file
Space / b
page down / up
/text
search forward; n next, N previous
g / G
jump to start / end
F
follow the file like tail -f; Ctrl+C to stop
q
quit
-N
show line numbers
-S
do not wrap long lines (scroll sideways)
less /var/log/syslog

read a log

journalctl -u nginx | less -S

wide lines without wrapping

head

Show the first lines of a file

Linux · macOS#
head [-n N] [-c N] file...
-n N
first N lines (default 10)
-c N
first N bytes
-n -N
everything except the last N lines (GNU)
head -n 20 data.csv

header and first rows

head -c 512 file.bin | xxd

peek at the start of a binary

macOS/BSD: macOS head does not accept negative counts.

tail

Show the end of a file, or follow it live

Linux · macOS#
tail [-n N] [-f|-F] file...
-n N
last N lines (default 10)
-n +N
start at line N
-f
keep printing as the file grows
-F
follow by name, survives log rotation
tail -n 50 app.log

last 50 lines

tail -F /var/log/nginx/access.log

watch a log that gets rotated

tail -n +2 data.csv

skip a CSV header line

nano

Edit a file with a simple editor

Linux#
nano file
Ctrl+O
save
Ctrl+X
exit
Ctrl+W
search
Ctrl+K / Ctrl+U
cut line / paste
Alt+U
undo
sudo nano /etc/hosts

edit a system file

The shortcut bar at the bottom of the screen shows the keys; ^ means Ctrl and M- means Alt.

vim

Edit a file with vim (and how to quit)

Linux · macOS#
vim file
i
insert mode (type text)
Esc
back to normal mode
:w / :q / :wq
save / quit / save and quit
:q!
quit without saving
/text
search; n for next
dd / yy / p
delete line / copy line / paste
u / Ctrl+R
undo / redo
vim +42 app.py

open at line 42

diff

Compare two files or directories

Linux · macOS#
diff [-u] [-r] [-q] [-w] a b
-u
unified format (what patch and git use)
-r
compare directories recursively
-q
only say whether files differ
-w
ignore all whitespace
-y
side by side
diff -u old.conf new.conf

readable change view

diff -rq dir1/ dir2/

which files differ between two trees

diff <(sort a.txt) <(sort b.txt)

compare after sorting, via process substitution

tee

Write output to a file and the screen at once

Linux · macOS#
tee [-a] file...
-a
append instead of overwrite
make 2>&1 | tee build.log

watch a build and keep a log

echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf

append to a root-owned file (sudo echo >> fails because the shell does the redirect)

printf

Print formatted text reliably

Linux · macOS#
printf 'format' args...
printf '%s\n' "$var"

print a variable safely, even if it starts with -n

printf '%-12s %6.2f\n' disk 93.5

aligned columns

echo -e and echo -n behave differently between shells; printf behaves the same everywhere.

column

Align output into a table

Linux · macOS#
column -t [-s separators]
column -t -s, data.csv | less -S

read a simple CSV as a table

mount | column -t

line up whitespace-separated output

xxd / hexdump

View a file as hex

Linux · macOS#
xxd file | hexdump -C file
xxd image.png | head

hex and ASCII side by side

hexdump -C file.bin | head

canonical hex dump

xxd is part of the vim packages; hexdump is in util-linux (bsdextrautils on newer Debian/Ubuntu).

strings

Pull readable text out of a binary file

Linux · macOS#
strings [-n min] file
-n N
only strings at least N characters long (default 4)
strings -n 8 firmware.bin | less

find version strings, URLs, paths

Part of binutils on Linux.

Permissions & ownership

Read and change who can read, write and execute a file.

ls -l

Read a permission string like -rwxr-x---

Linux · macOS#
ls -l file
ls -l deploy.sh

-rwxr-x--- 1 alice devs 812 Oct 18 09:12 deploy.sh

First character is the type (- file, d directory, l symlink). Then three triplets: owner, group, everyone else. r=4, w=2, x=1, so rwxr-x--- is 750.

chmod

Change permissions with octal numbers

Linux · macOS#
chmod [-R] mode file...
-R
apply to a directory and everything inside
-v
report each change
chmod 644 index.html

owner read/write, everyone read: normal files

chmod 755 deploy.sh

owner all, others read/execute: scripts and directories

chmod 600 ~/.ssh/id_ed25519

private key readable only by you; ssh refuses looser keys

chmod u+x

Change permissions with letters (u/g/o +/-)

Linux · macOS#
chmod [ugoa][+-=][rwxX] file...
chmod +x script.sh

make a script executable

chmod go-w file

remove write for group and others

chmod -R u=rwX,go=rX site/

capital X sets execute only on directories and files that already had it

chmod -R 777

Why chmod -R 777 is the wrong fix

DangerousLinux · macOS#
chmod -R 777 dir
find site/ -type d -exec chmod 755 {} +

the right fix, part 1: directories 755

find site/ -type f -exec chmod 644 {} +

part 2: files 644

Danger: Gives every user and every compromised service on the machine write access to the whole tree, marks all files executable, and makes OpenSSH refuse keys under a 777 ~/.ssh. Applied to /etc or /usr it can break sudo and the system. Fix ownership with chown instead, or use the two find commands.

chown

Change the owner and group of files

DangerousLinux · macOS#
chown [-R] user[:group] file...
-R
recursive
user:
with a trailing colon and no group, also set the group to the user's login group
sudo chown -R www-data:www-data /var/www/site

give a web root to the web server user

sudo chown "$USER": file.txt

take ownership of a file root created

Danger: sudo chown -R on /, /usr or /etc wipes setuid bits and ownership that sudo, passwd and services rely on; recovery usually means a reinstall. Double-check the path.

chgrp

Change only the group of files

Linux · macOS#
chgrp [-R] group file...
chgrp -R developers /srv/shared

hand a shared folder to a team group

umask

Set default permissions for new files

Linux · macOS#
umask [mask]
umask

show the current mask, commonly 0022

umask 077

new files 600 and directories 700 in this shell

The mask removes bits: files start from 666 and directories from 777, so 022 gives 644 and 755.

sudo

Run a command as root (or another user)

Linux · macOS#
sudo [-u user] [-i] command
-i
root login shell
-u user
run as that user instead of root
-l
list what you are allowed to run
-k
forget cached credentials
sudo systemctl restart nginx

one admin command

sudo !!

rerun the previous command with sudo

sudo -u postgres psql

run as a service user

su

Switch to another user

Linux · macOS#
su [-] [user]
su - deploy

log in as deploy with its environment

sudo su -

root shell when root has no password (sudo -i does the same)

setfacl / getfacl

Grant one extra user access with ACLs

Linux#
setfacl -m u:user:perms file
-m
modify (add) an entry
-x
remove an entry
-b
remove all ACL entries
-R
recursive
-d
set default ACL inherited by new files
setfacl -m u:alice:rw report.xlsx

alice can read and write, without changing group

getfacl report.xlsx

show all entries

setfacl -R -m g:devs:rwX /srv/app

a group gets access to a whole tree

A + at the end of the ls -l permission string means the file has an ACL.

chattr / lsattr

Make a file immutable

Linux#
chattr +i|-i file
sudo chattr +i /etc/resolv.conf

nobody, not even root, can change or delete it until -i

lsattr /etc/resolv.conf

show attributes; i means immutable

Supported on ext4, XFS, Btrfs. If "Operation not permitted" appears even as root, check lsattr.

Processes & jobs

Find what is running, stop it, and run things in the background.

ps

List running processes

Linux · macOS#
ps aux | ps -ef | ps -o fields -p PID
aux
all processes with CPU, memory, user (BSD style)
-ef
all processes with parent PID (System V style)
-o
choose columns
--sort=-%mem
sort by memory, highest first (procps)
ps aux | grep [n]ginx

the [n] trick keeps grep itself out of the results

ps aux --sort=-%mem | head

top memory users

ps -o pid,ppid,etime,cmd -p 1234

how long a process has been running

macOS/BSD: macOS ps has no --sort; use ps aux -m (by memory) or ps aux -r (by CPU).

pstree

Show processes as a parent/child tree

Linux#
pstree [-p] [user|PID]
pstree -p

with PIDs

pstree -p 1234

what a process has spawned

top

Watch CPU and memory use live

Linux · macOS#
top [-u user] [-p PID] [-b -n 1]
P / M
sort by CPU / memory (Linux top)
1
show each CPU core
k
kill a process by PID
q
quit
-b -n 1
one batch snapshot, for scripts
top -b -n 1 | head -20

snapshot you can paste into a ticket

macOS/BSD: macOS top is a different program: sort with top -o cpu or top -o mem.

htop

Friendlier interactive process viewer

Linux#
htop [-u user]
F3 or /
search
F4
filter
F6
choose sort column
F9
send a signal (kill)
t
tree view
htop -u www-data

only one user's processes

Install with apt install htop / dnf install htop.

pgrep

Find process IDs by name

Linux · macOS#
pgrep [-a] [-f] [-u user] pattern
-a
show the full command line (Linux procps)
-f
match against the full command line, not just the name
-u user
only that user's processes
-x
exact name match
pgrep -a nginx

PIDs and command lines

pgrep -f 'python manage.py runserver'

match a script by its arguments

macOS/BSD: On macOS, -a means "include ancestors"; use pgrep -lf to see names and arguments.

pkill

Stop processes by name

Linux · macOS#
pkill [-signal] [-f] [-u user] pattern
pkill -f "node server.js"

stop one Node app

pkill -HUP nginx

send a reload signal

pgrep -af node

preview what -f would match before killing

Patterns are regular expressions matched anywhere in the name; pkill node also hits nodejs-helper. Preview with pgrep first.

kill

Send a signal to a process (stop, kill, reload)

Linux · macOS#
kill [-signal] PID...
(none) / -15 / -TERM
ask the process to exit cleanly (default)
-9 / -KILL
force kill; cannot be caught, no cleanup
-1 / -HUP
many daemons reload their config
-l
list signal names
kill 1234

polite stop

kill -9 1234

last resort when TERM is ignored

kill %1

kill job 1 of this shell

Try plain kill first and give it a few seconds: -9 skips cleanup, so temp files, locks and half-written data can be left behind.

killall

Kill every process with an exact name

Linux · macOS#
killall [-signal] name
killall firefox

all Firefox processes

On Linux this is in the psmisc package.

Ctrl+Z, bg, fg, jobs

Pause a command and continue it in the background

Linux · macOS#
Ctrl+Z; bg [%n]; fg [%n]; jobs
jobs

list jobs in this shell with their numbers

bg %1

resume job 1 in the background

fg %1

bring it back to the foreground

Ctrl+Z suspends the foreground program (it stops running until bg or fg).

&

Start a command in the background

Linux · macOS#
command &
./long-export.sh > export.log 2>&1 &

start it and get the prompt back

wait

in a script, wait for background jobs to finish

nohup

Keep a command running after you log out

Linux · macOS#
nohup command > log 2>&1 &
nohup ./server > server.log 2>&1 &

survives closing the SSH session

For anything long-lived, a systemd service (see Services & logs) or tmux is more robust.

disown

Detach an already-running job from the shell

Linux · macOS#
disown [-h] [%n]
disown -h %1

you forgot nohup: background it with bg, then disown it

tmux

Keep a terminal session alive across disconnects

Linux#
tmux new -s name | tmux attach -t name
Ctrl+B d
detach (session keeps running)
Ctrl+B c
new window
Ctrl+B % / Ctrl+B "
split left/right / top/bottom
tmux ls
list sessions
tmux new -s deploy

start a named session

tmux attach -t deploy

reconnect after SSH drops

Install with your package manager. GNU screen is the older alternative (Ctrl+A d to detach, screen -r to reattach).

nice / renice

Run a command at lower (or higher) CPU priority

Linux · macOS#
nice -n N command | renice -n N -p PID
nice -n 19 tar -czf backup.tgz /data

background-friendly backup

sudo renice -n -5 -p 1234

raise priority of a running process

Niceness runs from -20 (highest priority) to 19 (lowest). Only root can go below 0.

timeout

Kill a command if it runs too long

Linux#
timeout [-s signal] duration command
timeout 30s curl -s https://example.com/health

give up after 30 seconds

timeout -s KILL 5m ./job.sh

hard kill after five minutes

Exits with status 124 when the time limit was hit.

watch

Rerun a command every few seconds

Linux#
watch [-n seconds] [-d] command
watch -n 2 df -h

watch disk space fill up

watch -d 'ss -s'

-d highlights what changed

lsof

See which process has a file or port open

Linux · macOS#
lsof [-i :port] [-p PID] [file]
sudo lsof -i :8080

what is listening on port 8080

sudo lsof +L1

deleted files still held open (disk full but du shows nothing)

lsof -p 1234

every file one process has open

fuser

Find (or kill) what is using a mount or port

DangerousLinux#
fuser [-v] [-m] [-k] target
sudo fuser -vm /mnt/usb

why umount says "target is busy"

sudo fuser -v 8080/tcp

process on a TCP port

Danger: fuser -k kills every process using the target with no prompt; on a mount point that can include your own shell.

time

Measure how long a command takes

Linux · macOS#
time command
time ./build.sh

real = wall clock, user + sys = CPU time

strace

Trace the system calls a program makes

Linux#
strace [-f] [-e trace=...] command | strace -p PID
strace -f -e trace=openat ./app 2>&1 | grep -v ENOENT

which files does it actually open?

sudo strace -p 1234

what is a hung process stuck on?

System info & resources

OS version, kernel, CPU, memory, hardware, time and load.

uname

Show kernel version and architecture

Linux · macOS#
uname [-a|-r|-m]
-a
everything
-r
kernel release
-m
machine architecture: x86_64, aarch64
uname -r

e.g. 6.8.0-45-generic

uname -m

which binary to download: x86_64 or aarch64

cat /etc/os-release

Find the Linux distribution and version

Linux#
cat /etc/os-release
cat /etc/os-release

NAME, VERSION_ID, ID for every systemd-era distro

lsb_release -a

Debian/Ubuntu alternative, if installed

macOS/BSD: macOS: sw_vers.

hostnamectl

Show or set the hostname

Linux#
hostnamectl [set-hostname name]
hostnamectl

hostname, OS, kernel, virtualisation

sudo hostnamectl set-hostname web01

rename the machine

uptime

Show uptime and load average

Linux · macOS#
uptime
uptime

load average over 1, 5 and 15 minutes

Load is the number of processes running or waiting. Compare it to the CPU count (nproc): a load of 4 on 4 cores is fully busy.

free

Check memory and swap usage

Linux#
free [-h]
free -h

look at the "available" column, not "free"

macOS/BSD: macOS: vm_stat, or top -l 1 | grep PhysMem.

Linux uses spare RAM as disk cache, so "free" looks low on a healthy machine. "available" is what programs can still get.

lscpu / nproc

Show CPU model and core count

Linux#
lscpu | nproc
lscpu

model, sockets, cores, threads, flags

make -j"$(nproc)"

use every available core

macOS/BSD: macOS: sysctl -n hw.ncpu and sysctl -n machdep.cpu.brand_string.

lspci / lsusb

List PCI and USB devices

Linux#
lspci [-k] | lsusb
lspci -k | grep -A3 -i vga

graphics card and the driver in use

lsusb

is the USB device detected at all?

dmidecode / lshw

Show RAM slots, BIOS and hardware details

Linux#
sudo dmidecode -t type | sudo lshw -short
sudo dmidecode -t memory | grep -i size

installed RAM per slot

sudo lshw -short

one line per device

dmesg

Read kernel messages (hardware, drivers, OOM kills)

Linux#
dmesg [-T] [-w] [--level=...]
-T
human-readable timestamps
-w
follow new messages
--level=err,warn
only problems
sudo dmesg -T | tail -30

what just happened (USB plugged in, disk errors)

sudo dmesg -T | grep -i 'out of memory'

did the OOM killer stop your app?

/proc

Read raw CPU, memory and process info from /proc

Linux#
cat /proc/<file>
grep MemAvailable /proc/meminfo

available memory in kB

grep -c ^processor /proc/cpuinfo

logical CPU count

cat /proc/1234/status

state and memory of one process

vmstat / iostat

See whether the bottleneck is CPU, memory or disk

Linux#
vmstat interval count | iostat -xz interval
vmstat 1 5

r = run queue, si/so = swapping, wa = waiting on I/O

iostat -xz 1

per-disk utilisation (%util) and latency (await)

iostat comes from the sysstat package.

date

Print or calculate dates and Unix timestamps

Linux · macOS#
date [+format] [-d 'description']
+%F
YYYY-MM-DD
+%s
Unix timestamp (seconds)
-d 'text'
parse a date: tomorrow, 2 days ago, @1700000000 (GNU)
-u
UTC
date '+%Y-%m-%d %H:%M:%S'

timestamp for logs

date +%s

seconds since 1970-01-01 UTC

date -d @1700000000

timestamp to date (GNU)

date -d '2 days ago' +%F

relative date (GNU)

macOS/BSD: macOS date has no -d. Use date -r 1700000000 for a timestamp, date -v-2d for "2 days ago", and date -j -f '%Y-%m-%d' 2026-10-18 +%s to parse.

timedatectl

Set the time zone and check clock sync

Linux#
timedatectl [set-timezone Zone]
timedatectl

local time, UTC, time zone, NTP synchronized?

timedatectl list-timezones | grep America

find the zone name

sudo timedatectl set-timezone America/Toronto

change it

Disks & filesystems

Free space, what is using it, partitions, mounting and formatting.

df

Check free disk space

Linux · macOS#
df [-h] [-T] [-i] [path]
-h
human-readable sizes
-T
show filesystem type (GNU)
-i
inode usage instead of bytes
df -h

all mounted filesystems

df -h .

the filesystem this directory is on

df -i

"No space left" with free bytes? You may be out of inodes

macOS/BSD: On macOS, -T filters by type rather than showing it.

du

Find what is using disk space

Linux · macOS#
du [-s] [-h] [-d depth] [path...]
-s
one total per argument
-h
human-readable
-d N
depth limit (GNU long form: --max-depth=N)
-x
stay on one filesystem
du -sh /var/log

size of one folder

du -h -d 1 | sort -h

subfolders, biggest last (works on GNU and macOS)

sudo du -xh -d 1 / | sort -h | tail

the biggest top-level directories

macOS/BSD: macOS du has -d but not --max-depth.

ncdu

Browse disk usage interactively

Linux#
ncdu [-x] [path]
sudo ncdu -x /

navigate with arrows, d to delete

Install with apt install ncdu / dnf install ncdu.

lsblk

List disks and partitions

Linux#
lsblk [-f]
-f
filesystem type, label, UUID, mount point
lsblk

disks, partitions, sizes, mount points

lsblk -f

which partition is ext4/xfs, and its UUID

macOS/BSD: macOS: diskutil list.

blkid

Get the UUID of a partition

Linux#
sudo blkid [device]
sudo blkid /dev/sdb1

UUID and TYPE, for /etc/fstab

findmnt

Show what is mounted where

Linux#
findmnt [target] | findmnt -T path
findmnt

mount tree with options

findmnt -T /var/lib/docker

which filesystem holds this path

sudo findmnt --verify

check /etc/fstab for errors before rebooting

mount

Mount a disk or partition

Linux#
mount [-t type] [-o options] device dir
sudo mount /dev/sdb1 /mnt/usb

mount a USB stick (directory must exist)

sudo mount -o remount,rw /

make a read-only root writable

sudo mount -t nfs nas:/export /mnt/nas

mount an NFS share

umount

Unmount a disk

Linux#
umount dir|device
sudo umount /mnt/usb

safe to unplug after this

sudo umount -l /mnt/nas

lazy: detach now, clean up when no longer busy

"target is busy" means a process (often your own shell sitting in that directory) is using it: find it with fuser -vm or lsof.

/etc/fstab

Mount a disk automatically at boot

Linux#
UUID=<uuid> <mountpoint> <type> <options> <dump> <pass>
UUID=3f6c...  /data  ext4  defaults,nofail  0  2

one line per filesystem; nofail lets boot continue if the disk is missing

sudo mount -a

mount everything in fstab now: test before rebooting

A typo in fstab can drop the next boot into emergency mode. Always run sudo mount -a (or findmnt --verify) after editing.

fdisk / parted

View or edit partition tables

DangerousLinux#
sudo fdisk -l | sudo fdisk /dev/sdX | sudo parted /dev/sdX print
sudo fdisk -l

every disk and its partitions

sudo parted /dev/sdb print

partition table type (gpt/msdos) and layout

Danger: Writing a partition table (w in fdisk, mklabel in parted) makes existing data on that disk unreachable. Confirm the device name with lsblk first; /dev/sda and /dev/sdb swap places easily.

mkfs

Format a partition

DangerousLinux#
sudo mkfs.<type> device
sudo mkfs.ext4 -L data /dev/sdb1

ext4 with a label

sudo mkfs.vfat -F 32 /dev/sdc1

FAT32 for a USB stick other devices can read

Danger: Erases the filesystem on the target immediately, with no confirmation if the device is not mounted. Check the device with lsblk -f; formatting /dev/sda instead of /dev/sda1, or the wrong disk, destroys it.

fsck

Check and repair a filesystem

DangerousLinux#
sudo fsck [-f] device
sudo fsck -f /dev/sdb1

force a full check of an unmounted partition

Danger: Never run fsck on a mounted read-write filesystem: it can corrupt it. Unmount first, or for the root filesystem boot from rescue media.

dd

Write an ISO to a USB stick or copy a raw disk

DangerousLinux · macOS#
dd if=input of=output bs=size [status=progress] [conv=fsync]
if= / of=
input / output file or device
bs=4M
block size; bigger is faster
status=progress
show bytes copied (GNU)
conv=fsync
flush to the device before exiting
sudo dd if=ubuntu.iso of=/dev/sdX bs=4M status=progress conv=fsync

make a bootable USB (replace sdX with the whole disk, not a partition)

dd if=/dev/zero of=test.img bs=1M count=100

create a 100 MB file

Danger: dd overwrites the of= target without asking. Swapping if= and of=, or picking the wrong /dev/sdX, wipes that disk. Check with lsblk right before running it.

macOS/BSD: macOS: target /dev/rdiskN (from diskutil list) after diskutil unmountDisk, use bs=4m, and press Ctrl+T for progress.

sync

Flush pending writes to disk

Linux · macOS#
sync
sync && sudo umount /mnt/usb

before pulling a USB drive

swapon / mkswap

Check or add swap space

Linux#
swapon --show | mkswap file && swapon file
swapon --show

current swap

sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

add a 2 GB swap file (add it to /etc/fstab to keep it)

On Btrfs a swap file needs extra steps (btrfs filesystem mkswapfile on recent btrfs-progs).

shred

Overwrite a file before deleting it

DangerousLinux#
shred [-u] [-n passes] file
shred -u -n 3 secrets.txt

overwrite three times, then delete

Danger: Irreversible. Also note it is not reliable on SSDs, journaling or copy-on-write filesystems (Btrfs, ZFS) or anything with snapshots; use full-disk encryption for real protection.

Networking

IP addresses, routes, open ports, HTTP requests, DNS, SSH and file transfer.

ip addr

Show IP addresses of this machine

Linux#
ip [-br] addr [show dev IFACE]
ip -br a

one line per interface: name, state, addresses

ip a show eth0

one interface in detail

hostname -I

just the IP addresses

macOS/BSD: macOS: ifconfig, or ipconfig getifaddr en0.

ip replaces the old ifconfig (net-tools), which many distributions no longer install.

ip route

Show the routing table and default gateway

Linux#
ip route [get ADDRESS] | ip route add NET via GW
ip r

routes; "default via" is the gateway

ip route get 1.1.1.1

which interface and gateway a destination uses

sudo ip route add 10.20.0.0/16 via 192.168.1.1

add a route (lost on reboot)

ip link

Bring a network interface up or down

DangerousLinux#
ip link [set IFACE up|down]
ip link

interfaces and MAC addresses

sudo ip link set eth0 down && sudo ip link set eth0 up

bounce an interface

Danger: Taking down the interface you are connected through over SSH cuts your session.

ss

List listening ports and open connections

Linux#
ss [-t] [-u] [-l] [-p] [-n]
-t / -u
TCP / UDP
-l
listening sockets only
-p
show the process (sudo to see other users')
-n
numeric ports, no DNS
-s
summary counts
sudo ss -tulpn

every listening port and its program

ss -tn state established '( dport = :443 )'

established connections to port 443

ss -s

connection totals

macOS/BSD: macOS: lsof -iTCP -sTCP:LISTEN -n -P, or netstat -an.

ss replaces netstat; netstat -tulpn still works where net-tools is installed.

curl

Make an HTTP request or download a file

Linux · macOS#
curl [options] URL
-I
headers only (HEAD request)
-L
follow redirects
-o file / -O
save to a file / save with the remote name
-s / -S
silent / but still show errors
-f
fail (exit 22) on HTTP 4xx/5xx instead of printing the error page
-v
verbose: request and response headers, TLS
curl -I https://example.com

status line and headers

curl -LO https://example.com/file.tar.gz

download, following redirects

curl -s -o /dev/null -w '%{http_code}\n' https://example.com

just the status code

curl -fsSL https://example.com/install.sh -o install.sh

download a script to read before running it

curl -d

Send a POST request with JSON

Linux · macOS#
curl -H 'Content-Type: application/json' -d 'json' URL
-d data
request body; implies POST
--data-binary @file
send a file as-is
-H header
add a header
-X METHOD
PUT, PATCH, DELETE…
-u user:pass
basic auth
curl -H 'Content-Type: application/json' -d '{"name":"Ada"}' https://api.example.com/users

POST JSON

curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me

double quotes so $TOKEN expands

curl -X DELETE https://api.example.com/users/42

other methods

wget

Download files (with resume)

Linux#
wget [options] URL
-c
resume a partial download
-O file
save as (use -O- for stdout)
-q
quiet
-r -np
recursive, never ascend to the parent directory
wget -c https://example.com/big.iso

resume after a dropped connection

wget -qO- https://example.com/data.json | jq .

pipe straight into another command

macOS/BSD: Not on stock macOS; use curl -LO (curl -C - -LO to resume).

dig

Look up DNS records

Linux · macOS#
dig [@server] name [type] [+short]
dig +short example.com

just the A records

dig MX example.com

mail servers

dig @1.1.1.1 example.com

ask a specific resolver (bypass local cache)

dig -x 8.8.8.8

reverse lookup

dig +trace example.com

follow delegation from the root

Package name: dnsutils (Debian/Ubuntu) or bind-utils (Fedora/RHEL).

host / nslookup

Quick DNS lookup

Linux · macOS#
host name | nslookup name [server]
host example.com

A, AAAA and MX in one short answer

nslookup example.com 8.8.8.8

the same command exists on Windows

resolvectl

Check DNS servers in use and flush the DNS cache

Linux#
resolvectl [status|query|flush-caches]
resolvectl status

DNS servers per interface

sudo resolvectl flush-caches

flush systemd-resolved cache

macOS/BSD: macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder.

Only on systems using systemd-resolved (Ubuntu, Fedora by default).

ping

Test whether a host is reachable

Linux · macOS#
ping [-c count] [-i interval] host
-c N
stop after N packets
-i sec
interval between packets
-4 / -6
force IPv4 / IPv6
ping -c 4 example.com

four packets, then a summary

On Linux and macOS ping runs until Ctrl+C unless you pass -c (Windows stops after 4). No reply does not always mean down: many hosts block ICMP.

traceroute / tracepath

See the route packets take to a host

Linux · macOS#
traceroute [-n] host | tracepath host
traceroute -n example.com

hops without DNS lookups

tracepath example.com

no root needed, also reports MTU

macOS/BSD: macOS has traceroute but not tracepath.

traceroute may need installing on Linux; tracepath (iputils) usually is there.

mtr

Find where packet loss happens

Linux#
mtr [-r] [-w] [-c count] host
mtr -rw -c 100 example.com

report after 100 probes per hop

Combines ping and traceroute. Loss at one middle hop only is usually that router de-prioritising ICMP; loss that continues to the end is real.

ssh

Log in to a remote machine

Linux · macOS#
ssh [-p port] [-i key] [-J jump] user@host [command]
ssh alice@203.0.113.10

log in

ssh -p 2222 alice@host

non-standard port

ssh alice@host 'df -h'

run one command and return

ssh -J bastion.example.com alice@10.0.0.5

hop through a bastion host

ssh -L / -R / -D

Forward a port through SSH (tunnel)

Linux · macOS#
ssh -L local:host:remote user@server
-L 5432:localhost:5432
local port 5432 reaches port 5432 on the server
-R 8080:localhost:3000
server port 8080 reaches your local 3000
-D 1080
SOCKS proxy on local port 1080
-N
no remote command, just the tunnel
ssh -N -L 5432:localhost:5432 alice@db-host

connect your local DB client to a remote Postgres

ssh -N -D 1080 alice@host

browse through the server

ssh-keygen

Create an SSH key and install it on a server

Linux · macOS#
ssh-keygen -t ed25519 [-C comment] | ssh-copy-id user@host
ssh-keygen -t ed25519 -C "you@example.com"

new key pair in ~/.ssh

ssh-copy-id alice@host

append your public key to the server's authorized_keys

ssh-keygen -R host

remove an old host key after a server rebuild ("REMOTE HOST IDENTIFICATION HAS CHANGED")

~/.ssh/config

Save SSH connection settings as a short alias

Linux · macOS#
Host alias / HostName / User / Port / IdentityFile
Host web
  HostName 203.0.113.10
  User deploy
  Port 2222
  IdentityFile ~/.ssh/id_ed25519

then just: ssh web (scp and rsync use it too)

scp

Copy files to or from a server over SSH

Linux · macOS#
scp [-r] [-P port] source... dest
-r
copy directories
-P port
port (capital P; ssh uses lowercase -p)
-i key
identity file
scp report.pdf alice@host:/tmp/

upload

scp alice@host:/var/log/app.log .

download

scp -r -P 2222 site/ alice@host:~/

upload a folder on another port

Since OpenSSH 9.0, scp uses the SFTP protocol underneath. For large or repeated copies rsync is faster because it only sends changes.

rsync

Sync folders locally or over SSH

Linux · macOS#
rsync [-avz] [--progress] [-n] source dest
-a
archive: recursive, keep permissions, times, links
-v
verbose
-z
compress in transit
-P
progress and resume partial files
-n
dry run: show what would happen
--exclude 'pattern'
skip files
-e 'ssh -p 2222'
custom SSH options
rsync -avzP site/ deploy@host:/srv/site/

trailing slash on site/ copies its contents, not the folder itself

rsync -av --exclude 'node_modules' ./ /backup/app/

local backup without the junk

macOS/BSD: Recent macOS ships openrsync instead of rsync; common flags work, some rarer ones do not.

src/ means "the contents of src"; src without a slash means "the folder src". Getting this wrong nests folders (dest/src/…).

rsync --delete

Make the destination an exact mirror

DangerousLinux · macOS#
rsync -av --delete source/ dest/
rsync -avn --delete site/ /srv/site/

dry run first: lines starting "deleting" show what would go

rsync -av --delete site/ /srv/site/

then the real run

Danger: Deletes everything in dest that is not in source. Swapping source and dest, or an empty/wrong source path, wipes the destination. Always run with -n first.

nc

Test whether a TCP port is open

Linux · macOS#
nc -zv host port | nc -l port
nc -zv db.internal 5432

"succeeded" or "Connection refused"/timeout

nc -l 9000

listen on a port to test a firewall from the other side

Flags differ between netcat variants: the traditional one needs nc -l -p 9000.

ufw

Manage the firewall on Ubuntu

DangerousLinux#
sudo ufw [status|allow|deny|delete|enable]
sudo ufw status verbose

rules and default policy

sudo ufw allow 22/tcp

allow SSH (do this before enable)

sudo ufw allow from 10.0.0.0/8 to any port 5432

one port, one network

Danger: sudo ufw enable on a remote server without first allowing SSH (ufw allow OpenSSH or 22/tcp) locks you out.

firewall-cmd

Manage the firewall on Fedora/RHEL

Linux#
sudo firewall-cmd [--permanent] --add-service|--add-port ...
sudo firewall-cmd --list-all

active zone and rules

sudo firewall-cmd --permanent --add-service=https && sudo firewall-cmd --reload

open HTTPS permanently

sudo firewall-cmd --permanent --add-port=8080/tcp

open a port

Without --permanent a rule lasts until the next reload or reboot.

nmcli

Manage Wi-Fi and connections (NetworkManager)

Linux#
nmcli [device|connection] ...
nmcli device status

interfaces and whether they are connected

nmcli device wifi list

nearby networks

nmcli device wifi connect 'MyWiFi' password 'secret'

join a network

Packages: apt, dnf, pacman, zypper

The same task in the four main package managers: Debian/Ubuntu (apt), Fedora/RHEL (dnf), Arch (pacman), openSUSE (zypper). Prefix with sudo for anything that changes the system.

Taskapt (Debian, Ubuntu)dnf (Fedora, RHEL)pacman (Arch)zypper (openSUSE)
Refresh package lists #
dnf refreshes automatically when metadata is stale. On Arch, never run pacman -Sy followed by -S pkg: partial upgrades are unsupported; use -Syu.
apt update
dnf makecache
pacman -Sy
zypper refresh
Upgrade everything #
openSUSE Tumbleweed uses zypper dup. apt full-upgrade also removes packages when needed to complete the upgrade.
apt update && apt upgrade
dnf upgrade
pacman -Syu
zypper update
Install a package #
apt install nginx
dnf install nginx
pacman -S nginx
zypper install nginx
Install a downloaded package file #
The ./ tells apt it is a file, not a package name, so dependencies get resolved (dpkg -i does not).
apt install ./app.deb
dnf install ./app.rpm
pacman -U app.pkg.tar.zst
zypper install ./app.rpm
Remove a package #
apt remove nginx
dnf remove nginx
pacman -R nginx
zypper remove nginx
Remove with config files / unused dependencies #
apt purge nginx && apt autoremove
dnf remove nginx && dnf autoremove
pacman -Rns nginx
zypper remove --clean-deps nginx
Show package details #
apt show nginx
dnf info nginx
pacman -Si nginx (installed: -Qi)
zypper info nginx
List installed packages #
apt list --installed
dnf list --installed
pacman -Q
zypper search --installed-only
List available updates #
apt list --upgradable
dnf check-update
checkupdates (pacman-contrib)
zypper list-updates
Which package owns a file #
For files not installed yet: dnf provides /path, apt-file search path (needs apt-file), pacman -F path.
dpkg -S /usr/bin/curl
rpm -qf /usr/bin/curl
pacman -Qo /usr/bin/curl
rpm -qf /usr/bin/curl
List files a package installed #
dpkg -L nginx
rpm -ql nginx
pacman -Ql nginx
rpm -ql nginx
Hold a package at its current version #
On DNF 4, versionlock needs the versionlock plugin package.
apt-mark hold nginx
dnf versionlock add nginx
IgnorePkg = nginx in /etc/pacman.conf
zypper addlock nginx
Clear the package cache #
apt clean
dnf clean all
pacman -Sc
zypper clean

Users & groups

Who you are, who is logged in, and managing accounts.

whoami / id

Show your user name, UID and groups

Linux · macOS#
whoami | id [user]
id

uid, gid and every group you belong to

id alice

another user

who / w / last

See who is logged in and recent logins

Linux · macOS#
who | w | last [-n N]
w

who is on and what they are running

last -n 10

recent logins and reboots

sudo lastb -n 10

recent failed logins (Linux)

useradd

Create a user account

Linux#
sudo useradd [-m] [-s shell] [-G groups] user
-m
create the home directory (not the default on every distro)
-s /bin/bash
login shell
-G group1,group2
supplementary groups
sudo useradd -m -s /bin/bash alice && sudo passwd alice

create and set a password

On Debian/Ubuntu, adduser alice is the friendlier interactive wrapper that sets up the home directory and password.

usermod -aG

Add a user to a group

DangerousLinux#
sudo usermod -aG group user
sudo usermod -aG docker alice

let alice run docker

sudo usermod -aG sudo alice

admin on Debian/Ubuntu (the group is wheel on Fedora/RHEL/Arch)

Danger: Without -a, usermod -G replaces all of the user's supplementary groups, which can remove sudo access. Group changes apply at the next login (or run newgrp docker).

userdel

Delete a user account

DangerousLinux#
sudo userdel [-r] user
sudo userdel -r olduser

also removes the home directory and mail spool

Danger: -r deletes the home directory and its files permanently. Back it up first, or lock the account instead (passwd -l).

passwd

Change a password or lock an account

Linux#
passwd [user] | passwd -l|-u user
passwd

change your own

sudo passwd alice

reset someone else's

sudo passwd -l alice

lock password login (SSH keys may still work)

groupadd / gpasswd

Create a group or remove a user from one

Linux#
sudo groupadd name | sudo gpasswd -d user group
sudo groupadd developers

new group

sudo gpasswd -d alice developers

remove alice from it

groups / getent

List the members of a group or a user's groups

Linux#
groups [user] | getent group name
groups alice

alice's groups

getent group sudo

members of sudo (works with LDAP too)

getent passwd alice

account entry: UID, home, shell

visudo

Give a user sudo rights safely

Linux · macOS#
sudo visudo [-f /etc/sudoers.d/file]
sudo visudo -f /etc/sudoers.d/deploy

separate file per rule

deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp

one command without a password

visudo checks the syntax before saving. Editing /etc/sudoers with a normal editor and making a typo can lock everyone out of sudo.

chage

Check or set password expiry

Linux#
sudo chage [-l] [-M days] user
sudo chage -l alice

last change and expiry dates

sudo chage -M 90 alice

expire every 90 days

Archives & compression

tar, gzip, xz, zip and checksums.

tar -c

Create a .tar.gz archive

Linux · macOS#
tar -czf archive.tar.gz files...
-c
create
-z / -j / -J
gzip / bzip2 / xz compression
-f file
archive file name (put it right after f)
-v
list files as they go in
--exclude='pattern'
leave files out
tar -czf site-2026-10-18.tar.gz site/

gzip-compressed archive of a folder

tar -cJf logs.tar.xz --exclude='*.tmp' logs/

smaller xz archive, skipping temp files

tar -x

Extract a .tar.gz, .tar.xz or .tar.bz2

Linux · macOS#
tar -xf archive [-C dir]
-x
extract
-C dir
extract into dir (must exist)
--strip-components=1
drop the top-level folder
tar -xf archive.tar.gz

GNU tar and macOS tar detect the compression themselves

tar -xzf app.tar.gz -C /opt/app --strip-components=1

into a folder, without the wrapper directory

tar -t

List or extract one file from an archive

Linux · macOS#
tar -tf archive | tar -xf archive path/in/archive
tar -tf backup.tar.gz | less

see what is inside without extracting

tar -xf backup.tar.gz etc/nginx/nginx.conf

pull out one file (path as shown by -t)

tar --zstd

Fast compression with zstd

Linux · macOS#
tar --zstd -cf archive.tar.zst files...
tar --zstd -cf data.tar.zst data/

much faster than xz, similar ratio to gzip -9 or better

zstd -d file.zst

decompress a single file

Needs GNU tar 1.31+ (or a recent bsdtar) and the zstd program.

gzip / gunzip

Compress or decompress a single file

Linux · macOS#
gzip [-k] [-d] [-9] file
-k
keep the original
-d
decompress (same as gunzip)
-9
best compression
gzip -k access.log

creates access.log.gz, keeps the original

zcat access.log.gz | grep 404

read without decompressing to disk

zgrep 500 access.log.*.gz

grep rotated logs directly

Without -k, gzip replaces the file with the .gz version.

xz / bzip2

Higher compression for single files

Linux#
xz [-T0] [-9] [-d] file | bzip2 [-d] file
xz -T0 -k big.sql

all CPU cores, keep original

unxz big.sql.xz

decompress

zip

Create a .zip file

Linux · macOS#
zip -r archive.zip files... [-x pattern]
zip -r site.zip site/

zip a folder

zip -r site.zip site/ -x '*.git*' '*/node_modules/*'

exclude paths

zip -e encrypts with the legacy ZipCrypto scheme, which is weak. Use 7z or gpg for real secrecy.

unzip

Extract a .zip file

Linux · macOS#
unzip [-l] [-o] archive.zip [-d dir]
unzip files.zip -d files/

into a folder

unzip -l files.zip

list contents only

unzip -o files.zip

overwrite without prompting

7z

Work with .7z (and most other) archives

Linux#
7z a|x|l archive [files]
7z x archive.7z

extract with full paths

7z a -p backup.7z docs/

AES-256 encrypted archive (prompts for a password)

7z l archive.rar

list contents of many formats

Package: p7zip-full / 7zip on Debian/Ubuntu, p7zip or 7zip on Fedora/Arch.

sha256sum

Verify a download with a checksum

Linux#
sha256sum file | sha256sum -c SUMSFILE
sha256sum ubuntu.iso

compare with the hash on the download page

sha256sum -c SHA256SUMS --ignore-missing

check every file listed that you actually have

macOS/BSD: macOS: shasum -a 256 file (and shasum -a 256 -c SUMSFILE).

split

Split a large file into parts and join them

Linux · macOS#
split -b size file prefix
split -b 100M backup.tar.gz backup.part.

backup.part.aa, .ab, …

cat backup.part.* > backup.tar.gz

join them again

Text processing

Cut, filter, sort, count, transform and reshape text and JSON.

sed s///

Find and replace text

Linux · macOS#
sed 's/old/new/[g]' file
g
replace every match on the line, not just the first
-E
extended regex (groups without backslashes)
I
case-insensitive match (GNU)
sed 's/http:/https:/g' links.txt

print the result; the file is unchanged

sed 's|/usr/local|/opt|g' paths.txt

any delimiter, handy for paths

sed -E 's/([0-9]+)px/\1rem/g' style.css

reuse a captured group

sed -i

Edit a file in place

Linux · macOS#
sed -i[SUFFIX] 's/old/new/g' file
sed -i 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config

GNU sed

sed -i.bak 's/foo/bar/g' config.ini

keeps config.ini.bak; works on GNU and macOS

macOS/BSD: macOS sed requires an argument after -i: sed -i '' 's/foo/bar/g' file. GNU sed reads '' as the script, so the only form that works on both is -i.bak.

sed -n / d

Print or delete specific lines

Linux · macOS#
sed -n 'Np' | sed '/regex/d'
sed -n '100,120p' big.log

lines 100 to 120

sed '/^\s*#/d; /^$/d' app.conf

strip comments and blank lines (GNU \s)

sed -n '/BEGIN CERT/,/END CERT/p' bundle.pem

everything between two markers

awk

Print specific columns

Linux · macOS#
awk [-F sep] '{print $N}' file
-F sep
field separator (default: runs of spaces/tabs)
$1 … $NF
fields; $NF is the last, $0 the whole line
NR
line number
awk '{print $1}' access.log

first column (client IP)

awk -F: '{print $1, $7}' /etc/passwd

user and shell

awk -F, 'NR>1 {print $2}' data.csv

second CSV column, skip header (simple CSV only: no quoted commas)

awk (filter & sum)

Filter rows and total a column

Linux · macOS#
awk 'condition {action} END {action}' file
awk '$9 >= 500' access.log

lines where field 9 (status) is 5xx

awk '{s += $2} END {print s}' sales.txt

sum of column 2

awk '{c[$1]++} END {for (k in c) print c[k], k}' access.log | sort -rn | head

count per value, top 10

cut

Cut fields by a single-character delimiter

Linux · macOS#
cut -d DELIM -f LIST | cut -c RANGE
cut -d, -f1,3 data.csv

columns 1 and 3

cut -d: -f1 /etc/passwd

all user names

cut -c1-10 file

first 10 characters of each line

cut treats every delimiter as a field break, so two spaces mean an empty field. For space-aligned output use awk.

sort

Sort lines

Linux · macOS#
sort [-n] [-r] [-h] [-k N,N] [-t sep] [-u] file
-n
numeric
-r
reverse
-h
human sizes: 2K < 1M < 3G
-k 2,2
sort by field 2 only
-t ,
field separator
-u
drop duplicates
-V
version order: 1.9 < 1.10
du -sh * | sort -h

folders by size

sort -t, -k3,3nr data.csv

CSV by column 3, numeric, descending

sort -u emails.txt > unique.txt

sorted and de-duplicated

uniq

Count or remove duplicate lines

Linux · macOS#
sort file | uniq [-c] [-d] [-u]
-c
prefix each line with its count
-d
only lines that repeat
-u
only lines that appear once
sort ips.txt | uniq -c | sort -rn | head

top 10 most frequent lines

uniq only collapses adjacent duplicates, so sort first.

wc

Count lines, words or bytes

Linux · macOS#
wc [-l] [-w] [-c] [-m] file...
wc -l access.log

number of lines

ls | wc -l

how many entries in this folder

grep -c '' file

line count that includes a last line without a newline

tr

Translate or delete characters

Linux · macOS#
tr [-d] [-s] 'set1' ['set2']
tr 'a-z' 'A-Z' < in.txt

uppercase

tr -d '\r' < windows.txt > unix.txt

strip Windows CRLF line endings

tr -s ' ' < file

squeeze repeated spaces

tr '\n' ',' < list.txt

join lines with commas

tr only reads standard input, so redirect the file in with <.

xargs

Turn input lines into command arguments

Linux · macOS#
command | xargs [-0] [-n N] [-P N] [-I {}] cmd
-0
input is NUL-separated (pair with find -print0); safe with spaces
-n N
at most N arguments per command
-P N
run N commands in parallel
-I {}
replace {} with each input line
-r
do not run the command if input is empty (GNU)
find . -name '*.log' -print0 | xargs -0 rm -f

delete found files safely

cat urls.txt | xargs -n 1 -P 4 curl -sO

download 4 at a time

ls *.png | xargs -I {} cp {} /backup/{}

one command per item

macOS/BSD: GNU xargs runs the command once even with empty input unless you pass -r; macOS xargs skips empty input by default and accepts -r for compatibility.

jq

Read and filter JSON

Linux · macOS#
jq [-r] 'filter' file
.
pretty-print
-r
raw strings, no quotes
-c
compact, one line per result
.[]
iterate an array
select(cond)
keep matching items
jq . response.json

pretty-print

jq -r '.items[].name' data.json

one name per line

jq -r '.[] | select(.active) | .email' users.json

filter, then pick a field

curl -s https://api.github.com/repos/jqlang/jq | jq '.stargazers_count'

straight from an API

Install with apt/dnf install jq. Ships with recent macOS.

paste

Join lines or merge files side by side

Linux · macOS#
paste [-s] [-d delim] file...
paste -sd, ids.txt

all lines joined with commas

paste names.txt emails.txt

two files as two columns

comm

Lines common to (or unique to) two lists

Linux · macOS#
comm [-1] [-2] [-3] sorted1 sorted2
comm -12 <(sort a.txt) <(sort b.txt)

in both

comm -23 <(sort a.txt) <(sort b.txt)

only in a.txt

seq

Generate a sequence of numbers

Linux · macOS#
seq [first [step]] last
seq 1 5

1 to 5, one per line

seq -w 1 10

zero-padded: 01 … 10

for i in $(seq 1 3); do echo "run $i"; done

loop counter

base64

Encode or decode Base64

Linux · macOS#
base64 [-d] [-w 0] [file]
printf '%s' 'user:pass' | base64

encode (printf avoids a trailing newline)

echo dXNlcjpwYXNz | base64 -d

decode

base64 -w 0 image.png > image.b64

no line wrapping (GNU wraps at 76 chars)

macOS/BSD: macOS base64 does not wrap by default and has no -w. Use -d to decode (older macOS used -D).

iconv

Convert text encoding (e.g. to UTF-8)

Linux · macOS#
iconv -f FROM -t TO file
iconv -f WINDOWS-1252 -t UTF-8 old.csv > new.csv

fix mojibake from Excel exports

iconv -l

list supported encodings

shuf

Shuffle lines or pick random ones

Linux#
shuf [-n N] file
shuf -n 1 names.txt

one random line

shuf -i 1-100 -n 5

five random numbers from 1 to 100

Services & logs

systemd services: start, stop, enable at boot, and read their logs with journalctl.

systemctl status

Check whether a service is running

Linux#
systemctl status|is-active|is-enabled unit
systemctl status nginx

state, PID, memory and the last log lines

systemctl is-active --quiet nginx && echo up

for scripts

systemctl start

Start, stop, restart or reload a service

Linux#
sudo systemctl start|stop|restart|reload unit
sudo systemctl restart nginx

full restart

sudo systemctl reload nginx

re-read config without dropping connections (if the service supports it)

Test config before restarting where possible, e.g. sudo nginx -t, so a typo does not take the site down.

systemctl enable

Start a service at boot (or stop it starting)

Linux#
sudo systemctl enable|disable [--now] unit
sudo systemctl enable --now docker

enable at boot and start now

sudo systemctl disable --now cups

stop now and at boot

sudo systemctl mask bluetooth

block it from being started at all (unmask to undo)

systemctl list-units

List running and failed services

Linux#
systemctl list-units [--type=service] [--state=…] | systemctl --failed
systemctl --failed

what broke

systemctl list-units --type=service --state=running

what is running

systemctl list-unit-files --state=enabled

what starts at boot

systemctl edit

Change a service unit (and reload systemd)

Linux#
sudo systemctl edit unit | sudo systemctl daemon-reload
sudo systemctl edit nginx

drop-in override that survives package upgrades

systemctl cat nginx

the unit plus all overrides

sudo systemctl daemon-reload

needed after editing unit files by hand

.service file

Run your own app as a service

Linux#
/etc/systemd/system/<name>.service
[Unit]
Description=My app
After=network.target

[Service]
User=app
WorkingDirectory=/srv/app
ExecStart=/srv/app/bin/server
Restart=on-failure

[Install]
WantedBy=multi-user.target

then: sudo systemctl daemon-reload && sudo systemctl enable --now myapp

journalctl

Read service and system logs

Linux#
journalctl [-u unit] [-f] [-n N] [--since …] [-p level] [-b]
-u unit
one service
-f
follow
-n N
last N lines
--since '1 hour ago'
also: today, yesterday, 2026-10-18 09:00
-p err
priority err and worse
-b / -b -1
this boot / previous boot
-e
jump to the end
journalctl -u nginx -f

follow one service

journalctl -u myapp --since '10 min ago'

recent window

journalctl -b -1 -p err

errors from the boot before the crash

journalctl --vacuum

Shrink the systemd journal

Linux#
journalctl --disk-usage | sudo journalctl --vacuum-size=SIZE | --vacuum-time=TIME
journalctl --disk-usage

how much space logs take

sudo journalctl --vacuum-size=500M

keep at most 500 MB of archived logs

sudo journalctl --vacuum-time=14d

drop archived logs older than two weeks

For a permanent limit set SystemMaxUse= in /etc/systemd/journald.conf.

/var/log

Find classic log files

Linux#
sudo tail -f /var/log/<file>
sudo tail -f /var/log/syslog

general log on Debian/Ubuntu (/var/log/messages on Fedora/RHEL)

sudo tail -f /var/log/auth.log

logins and sudo on Debian/Ubuntu (/var/log/secure on RHEL)

Some distributions (Fedora, Arch) keep logs only in the journal unless rsyslog is installed.

shutdown / reboot

Reboot or power off

Linux#
sudo shutdown -r|-h [now|+min|hh:mm] [message] | sudo reboot
sudo reboot

reboot now

sudo shutdown -r +10 'Kernel update'

reboot in 10 minutes, warning logged-in users

sudo shutdown -c

cancel a scheduled shutdown

sudo shutdown -h now

power off

Scheduling: cron, at, timers

Run commands on a schedule or once later.

cron syntax

Write a cron schedule line

Linux · macOS#
minute hour day-of-month month day-of-week command
30 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

every day at 02:30, keep output

*/15 * * * * /usr/local/bin/check.sh

every 15 minutes

0 9 * * 1-5 /usr/local/bin/report.sh

09:00 Monday to Friday

@reboot /usr/local/bin/startup.sh

once at boot

Cron runs with a minimal PATH and no shell profile: use absolute paths. A % in the command must be escaped as \%.

crontab -r

Remove your crontab (careful)

DangerousLinux · macOS#
crontab -r
crontab -l > ~/crontab.bak && crontab -r

back up before removing

crontab ~/crontab.bak

restore from a file

Danger: Deletes every one of your cron jobs with no prompt, and r sits right next to e on the keyboard. Some cron implementations offer -i to ask first (crontab -ri).

/etc/cron.d

System-wide cron jobs

Linux#
/etc/crontab, /etc/cron.d/*, /etc/cron.{hourly,daily,weekly,monthly}/
0 3 * * * root /usr/local/bin/cleanup.sh

line in /etc/cron.d/cleanup: note the extra user field

ls /etc/cron.daily/

scripts run once a day by run-parts

On Debian and Ubuntu, files in /etc/cron.d whose names contain a dot are ignored, so name it backup, not backup.cron.

at

Run a command once at a later time

Linux#
at TIME | atq | atrm N
echo 'systemctl restart myapp' | sudo at 02:00

tonight at 2 am

echo './report.sh' | at now + 30 minutes

in half an hour

atq

pending jobs

atrm 3

cancel job 3

Needs the at package and the atd service running.

systemd timers

Schedule with systemd timers instead of cron

Linux#
systemctl list-timers | <name>.timer with OnCalendar=
systemctl list-timers

every timer and when it next fires

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target

backup.timer next to backup.service; Persistent catches up after downtime

systemd-analyze calendar 'Mon..Fri 09:00'

check what an OnCalendar expression means

sleep

Wait before the next command

Linux · macOS#
sleep N[s|m|h|d]
sleep 5 && curl -s localhost:8080/health

give a service time to start

sleep 2m

suffixes work on GNU and current macOS

Shell basics (bash)

Pipes, redirection, globbing, variables, history and the keys that save the most time. macOS uses zsh by default: nearly all of this works there too, except shopt, help and bash 4 features (macOS ships bash 3.2).

|

Send one command's output into another

Linux · macOS#
command1 | command2
ps aux | grep nginx

filter a listing

history | grep 'docker run'

find a command you ran before

set -o pipefail

in scripts: a pipeline fails if any part fails, not just the last

> and >>

Save output to a file (overwrite or append)

Linux · macOS#
command > file | command >> file
ls -l > listing.txt

overwrite (creates the file)

echo 'done' >> progress.log

append

set -o noclobber

make > refuse to overwrite existing files (>| forces)

The shell truncates the file before the command runs, so sort file > file leaves you with an empty file. Write to a new name, then mv.

2> and 2>&1

Redirect errors, or errors and output together

Linux · macOS#
command 2> err.log | command > all.log 2>&1 | command &> all.log
find / -name x 2>/dev/null

throw away error messages

./build.sh > build.log 2>&1

output and errors in one file (order matters: > first, then 2>&1)

./build.sh &> build.log

bash shorthand for the same

<, <<EOF, <<<

Feed input from a file, a block of text or a string

Linux · macOS#
command < file | command <<'EOF' … EOF | command <<< "string"
mysql mydb < dump.sql

file as input

cat > config.yml <<'EOF'
port: 8080
host: $HOST stays literal
EOF

quoting 'EOF' stops $variables expanding

grep -c a <<< "banana"

here-string

&& || ;

Run commands one after another, conditionally

Linux · macOS#
a && b | a || b | a ; b
make && sudo make install

install only if the build succeeded

cd /srv/app || exit 1

stop a script if cd fails

(cd frontend && npm run build)

parentheses: a subshell, so your directory does not change

$( ) $(( )) <( )

Use a command's output or do arithmetic

Linux · macOS#
$(command) | $((expression)) | <(command)
echo "Today is $(date +%A)"

command substitution

echo $(( 1024 * 4 ))

integer arithmetic

diff <(ls dir1) <(ls dir2)

process substitution: output as a temporary file

Backticks `command` are the old form of $(command); $( ) nests and reads better.

* ? [ ] { }

Match many files with wildcards

Linux · macOS#
* any string | ? one char | [abc] one of | {a,b} each word
ls *.jpg

all JPGs in this folder

rm photo-??.png

photo-01.png … photo-99.png

cp config.{yml,yml.bak}

brace expansion: cp config.yml config.yml.bak

shopt -s globstar; ls **/*.md

** matches any depth (bash 4+)

Globs do not match dotfiles by default (shopt -s dotglob changes that). If nothing matches, bash passes the pattern literally unless shopt -s nullglob is set.

' ' vs " "

Quote strings and variables correctly

Linux · macOS#
'literal' | "expands $var and $(cmd)" | \ escapes one character
echo 'Cost: $5'

single quotes: nothing expands

echo "Home is $HOME"

double quotes: variables expand, spaces are kept

cp "$file" "$dest/"

always quote variables that hold paths: spaces break unquoted ones

VAR=value

Set and use shell variables

Linux · macOS#
name=value (no spaces around =) | "$name" | "${name}"
name="Ada"; echo "Hi $name"

set and use

echo "${PORT:-8080}"

default when unset or empty

file=report.tar.gz; echo "${file%%.*}" "${file##*/}"

strip extension / strip directory

echo "${#name}"

string length

export

Set environment variables

Linux · macOS#
export NAME=value | NAME=value command | unset NAME
export NODE_ENV=production

visible to every program started from this shell

DEBUG=1 ./app

only for that one command

printenv | sort

list them

unset DEBUG

remove

Lost when the shell exits. Put exports in ~/.bashrc (interactive shells) or ~/.profile (login shells) to keep them.

$PATH

Add a folder to your PATH

Linux · macOS#
export PATH="dir:$PATH"
echo "$PATH" | tr : '\n'

one entry per line

export PATH="$HOME/.local/bin:$PATH"

prepend your own bin folder (add to ~/.bashrc)

hash -r

forget cached command locations after moving binaries

alias / source

Create shortcuts and reload your shell config

Linux · macOS#
alias name='command' | source file
alias ll='ls -alF'

add to ~/.bashrc to keep

source ~/.bashrc

apply changes without opening a new terminal (same as . ~/.bashrc)

\ls

run the real ls, bypassing an alias

$?

Check whether the last command succeeded

Linux · macOS#
echo $? (0 = success)
grep -q root /etc/passwd; echo $?

0 found, 1 not found, 2 error

set -euo pipefail

top of a script: stop on errors, unset variables and failed pipes

history, !!

Reuse previous commands

Linux · macOS#
history | !! | !$ | !N | !prefix | ^old^new
sudo !!

rerun the last command with sudo

cd !$

use the last argument of the previous command

!42

rerun line 42 of history

^foo^bar

rerun the last command with foo replaced by bar

With HISTCONTROL=ignorespace (default on Ubuntu as part of ignoreboth), a command typed with a leading space is not saved: useful for commands with secrets.

Ctrl+R, Ctrl+A …

Terminal keyboard shortcuts (bash/readline)

Linux · macOS#
Emacs-style readline keys (the default)
Ctrl+R
search history backwards; press again for older matches
Ctrl+A / Ctrl+E
start / end of line
Ctrl+U / Ctrl+K
cut to start / to end of line
Ctrl+W
cut the previous word
Ctrl+Y
paste what you cut
Alt+B / Alt+F
back / forward one word
Alt+.
insert the last argument of the previous command
Ctrl+L
clear the screen
Ctrl+C
cancel the running command
Ctrl+D
end of input; exits the shell on an empty line
Tab Tab
show completions
bind -P | less

list every key binding

man / --help

Get help for any command

Linux · macOS#
man [section] cmd | cmd --help | help builtin | apropos word
man rsync

full manual; / to search, q to quit

man 5 crontab

section 5: the file format, not the command

help cd

bash builtins have no man page of their own

apropos partition

which commands mention a word

#!/usr/bin/env bash

Write and run a shell script

Linux · macOS#
#!/usr/bin/env bash, chmod +x, ./script.sh
#!/usr/bin/env bash
set -euo pipefail
name="${1:-world}"
if [[ -f "$name.txt" ]]; then
  echo "found $name.txt"
fi
for f in *.log; do
  echo "$f"
done

arguments, a test, a loop

chmod +x script.sh && ./script.sh Ada

make executable and run

bash -x script.sh

trace each line as it runs

$1 … $9 are arguments, "$@" is all of them, $# is how many. [[ ]] is bash; use [ ] in POSIX sh scripts.

clear / reset

Clear or repair a garbled terminal

Linux · macOS#
clear | reset
reset

after cat-ing a binary file left the terminal printing junk

A static reference that runs in your browser; your searches are not sent anywhere. Browse all free tools.

Found a problem, or something that could be better? We read every message and we fix things quickly.

This Linux commands cheat sheet groups more than 200 commands by the job you are trying to do: moving around and managing files, permissions, processes, disk space, networking, packages, users, archives, searching, text processing, systemd services and logs, cron, and the bash basics that tie them together. Each entry gives the syntax, the flags you will actually use with a one-line explanation, copyable examples and, where it matters, a warning. Search by task (“free disk space”, “what is using port 8080”) or by command name, and link straight to any entry.

Flags follow the GNU coreutils, util-linux, procps-ng, iproute2, systemd and bash manuals, which is what Ubuntu, Debian, Fedora, RHEL, Arch and openSUSE ship. Where macOS behaves differently, a blue note says how, because sed -i, date -d, stat -c and xargs are the classic places where a script written on one breaks on the other. It is a reference for the common 90%, not a replacement for man pages: for rarely used options, man <command> is still the authority.

How to use

  1. Type a task or a command into the search box (press / to jump to it). All words must match, so “tar extract” narrows to the tar entries.
  2. Use the section chips to jump to a topic such as Networking or Packages, or tick “Dangerous only” to see every command that can destroy data or lock you out.
  3. Click Copy next to any example, then replace the sample file names and hosts with your own.
  4. Click the # next to an entry to get a direct link to it, for example /tools/linux-commands-cheat-sheet#rsync.
  5. Use Print / Save as PDF for a compact two-column copy without the site navigation.

Frequently asked questions

What are the most basic Linux commands to learn first?

Start with navigation and files: pwd, ls, cd, mkdir, cp, mv, rm, cat and less. Then man (help), sudo, grep and find for searching, chmod and chown for permissions, ps, top and kill for processes, df and du for disk space, and ssh and scp for remote machines. Learn pipes (|) and redirection (> and >>) early, because they let you combine everything else.

How do I check disk space on Linux?

Run df -h to see free space on every mounted filesystem, in human-readable units. To find what is using the space, run du -h -d 1 | sort -h inside a directory: it lists subfolders with the largest last. If df says the disk is full but du finds nothing, a deleted file is probably still held open by a process; sudo lsof +L1 lists them.

How do I find which process is using a port?

Run sudo ss -tulpn to list every listening TCP and UDP port with the program that owns it, or sudo lsof -i :8080 for one port. The sudo matters: without it you only see the names of your own processes. ss replaced netstat, which still works on systems that have the net-tools package installed.

What is the difference between kill and kill -9?

kill PID sends SIGTERM, which asks the process to exit and lets it clean up: flush files, release locks, close connections. kill -9 PID sends SIGKILL, which the kernel enforces immediately and the process cannot catch or ignore, so no cleanup happens. Use plain kill first, wait a few seconds, and keep -9 for processes that ignore it.

Why does sed -i not work on a Mac?

macOS ships BSD sed, where -i requires a backup extension argument. On macOS write sed -i '' 's/old/new/g' file; on Linux GNU sed the same command fails because it reads '' as the script. The form that works on both is sed -i.bak 's/old/new/g' file, which keeps a backup copy you can delete afterwards.

Are these commands the same on every Linux distribution?

Almost all of them are: the core tools come from the same GNU and util-linux projects everywhere. What differs is the package manager (apt on Debian and Ubuntu, dnf on Fedora and RHEL, pacman on Arch, zypper on openSUSE, shown side by side in the Packages table), some log file paths, the admin group (sudo vs wheel) and the firewall front end (ufw vs firewalld). Alpine and BusyBox-based systems use smaller versions of the tools with fewer flags.

The commands that can wreck a system, and the habit that prevents it

A short list of commands does almost all of the damage: rm -rf with a wrong or empty path, dd or mkfs pointed at the wrong disk, chmod -R 777 or chown -R on a system directory, rsync --delete or robocopy-style mirrors with source and destination swapped, crontab -r instead of crontab -e, usermod -G without -a, and find with -delete in the wrong position. Each is marked Dangerous in the sheet with the specific way it goes wrong.

The habit that prevents most of it is to run the harmless version first. Before rm -rf, run ls on the same path. Before find … -delete, run the same find with -print. Before rsync --delete, add -n for a dry run. Before dd or mkfs, run lsblk and read the device name twice. In scripts, write ${DIR:?} instead of $DIR so an empty variable aborts the script instead of turning a path into /.

Two shell settings help too: set -euo pipefail at the top of scripts stops on the first error instead of continuing with bad state, and set -o noclobber makes > refuse to overwrite an existing file.

GNU vs macOS (BSD): where the same command behaves differently

macOS is Unix, but its command-line tools come from FreeBSD rather than GNU, and the differences show up exactly when you copy a command from a Linux tutorial:

• sed -i needs an argument on macOS (sed -i '' …); -i.bak works on both.

• date has no -d on macOS; use date -r <epoch> to convert a timestamp and date -v-2d for relative dates.

• stat uses -f with different format letters instead of GNU -c.

• xargs on macOS skips empty input by default, while GNU xargs runs the command once unless given -r.

• du takes -d 1 on both, but only GNU accepts --max-depth; ps has no --sort on macOS (use ps aux -m or -r).

• head -n -5, tac, free, ip, ss, timeout and watch are missing from stock macOS.

If you need GNU behaviour on a Mac, Homebrew's coreutils, gnu-sed and findutils packages install the GNU versions with a g prefix (gsed, gdate, gstat, gxargs).

Reading permissions and picking the right chmod

ls -l shows permissions as ten characters, such as -rwxr-x---. The first is the type (- file, d directory, l link); the next nine are three groups of read, write and execute for the owner, the group and everyone else. As numbers, r is 4, w is 2 and x is 1, added per group, so rwxr-x--- is 750.

Typical modes: 644 for ordinary files, 755 for directories and scripts, 600 for private keys and files holding secrets, 700 for a private directory such as ~/.ssh. On a directory, x means “may enter”, which is why directories need it and plain files do not. The chmod calculator converts between the symbolic and octal forms if you want to check a mode before applying it.

Related free tools

  • CMD vs PowerShell vs Bash — The same task in Command Prompt, PowerShell and bash, side by side: files, grep, environment variables, processes, network, services and scripting.
  • Chmod Calculator — Tick read, write and execute boxes to get the octal mode and chmod command, or decode 755, 644 and rwxr-xr-x.
  • Cron Expression Generator — Build a cron schedule from dropdowns or paste one, read it in plain English, and see the next 10 run times in your zone and UTC. Free, in your browser.
  • Subnet Calculator — Network, broadcast, host range and mask for any CIDR; split networks with VLSM, expand IPv6 and find overlapping ranges.
  • Git Cheat Sheet — Git commands organised by what you want to do: undo the last commit, rename a branch, delete a remote branch. Modern switch/restore with classic equivalents and danger badges.