/user/kayd @ devops :~$ cat bash-cli.md

Bash CLI One-Liners Cheatsheet Bash CLI One-Liners Cheatsheet

QR Code linking to: Bash CLI One-Liners Cheatsheet
Karandeep Singh
Karandeep Singh
• 6 minutes

Summary

The command-line one-liners I use daily. find/xargs, grep, curl, jq, tar, disk and process triage, network checks, ssh/rsync, history tricks, and job control.

These are the one-liners I actually type on production boxes. Muscle-memory commands for finding files, chasing disk usage, poking APIs, and triaging misbehaving processes. When a one-liner turns into a script, the Bash Basics Cheatsheet covers making it robust.

find + xargs

Docs: find(1) man page

find /var/log -name '*.gz' -mtime +30 -delete        # old compressed logs
find . -type f -size +100M                           # large files
find . -name '*.sh' -exec chmod +x {} +              # batch chmod
find . -type f -name '*.yaml' | xargs grep -l 'image:'
find . -name '*.log' -print0 | xargs -0 rm --        # -print0/-0: safe with spaces
find . -type d -empty -delete                        # remove empty dirs
find /etc -newer /etc/hostname -type f               # changed since that file
ls *.png | xargs -P4 -I{} convert {} {}.webp         # -P4: 4 parallel jobs

grep

Docs: grep(1) man page

grep -r 'TODO' src/                    # recursive
grep -rn --include='*.tf' 'aws_s3' .   # line numbers, filter by glob
grep -E 'error|fatal|panic' app.log    # extended regex, alternation
grep -o 'req_id=[a-f0-9]*' app.log     # print only the match
grep -c '500' access.log               # count matching lines
grep -v '^#' config | grep -v '^$'     # strip comments and blanks
grep -B2 -A5 'Traceback' app.log       # context: 2 before, 5 after
grep -i -l 'password' -r . 2>/dev/null # files containing, case-insensitive

sort / uniq / wc Pipelines

Docs: sort(1) man page

sort file | uniq -c | sort -rn | head            # frequency count, descending
sort -t: -k3 -n /etc/passwd                      # numeric sort on field 3
sort -u hosts.txt                                # sort + dedupe in one
sort -h                                          # human sizes: 1K < 2M < 3G
uniq -d sorted.txt                               # only duplicated lines
wc -l < file                                     # line count, no filename
comm -23 <(sort a.txt) <(sort b.txt)             # lines in a but not b
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head   # top IPs

For heavier field surgery in these pipelines, see the Bash sed & awk Cheatsheet.

tar / gzip / zstd

Docs: tar(1) man page

tar czf app.tar.gz app/                # create gzip archive
tar xzf app.tar.gz -C /opt/           # extract to a directory
tar tzf app.tar.gz                    # list contents without extracting
tar czf - /data | ssh host 'cat > /backup/data.tar.gz'   # archive over ssh
tar --exclude='.git' --exclude='node_modules' -czf src.tar.gz .
tar caf app.tar.zst app/              # -a: pick compressor by extension
zstd -T0 bigfile                      # zstd on all cores; much faster than gzip
gzip -dk file.gz                      # decompress, keep original
zcat access.log.gz | grep ' 500 '     # search inside without extracting

curl

Docs: curl manpage on curl.se

curl -I https://example.com                       # headers only
curl -sfSL https://example.com/health             # silent, fail on HTTP errors
curl -o out.bin -L https://example.com/dl         # follow redirects, save
curl -w '%{http_code} %{time_total}s\n' -o /dev/null -s https://api.example.com
curl -X POST https://api.example.com/v1/deploys \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"service": "web", "version": "1.4.2"}'
curl --retry 5 --retry-delay 2 --retry-all-errors -sf https://flaky.example.com
curl -u user:pass ftp://host/file                 # basic auth
curl -x http://proxy:3128 https://example.com     # via proxy

Get new articles by email

One DevOps article a week, plus the 18-cheatsheet PDF pack. No spam, one click to leave.

jq Basics

Docs: jq Manual on jqlang.org

curl -s https://api.example.com/pods | jq .              # pretty-print
jq '.items[].metadata.name' pods.json                    # extract a field
jq -r '.token' resp.json                                 # -r: raw, no quotes
jq '.items | length' pods.json                           # count
jq '.[] | select(.status == "failed") | .id' jobs.json   # filter
jq '.[] | {name: .metadata.name, ip: .status.podIP}'     # reshape
jq -r '.[] | [.name, .cpu] | @tsv' nodes.json            # TSV for awk/column
jq -n --arg env "$ENV" '{environment: $env}'             # build JSON from shell
jq 'keys' obj.json                                       # what fields exist?

Disk & Process Triage

Docs: ps(1) man page

df -h                                  # filesystem usage
du -sh /var/* | sort -rh | head        # what's eating this disk
du -xh --max-depth=2 / | sort -rh | head -20   # -x: don't cross filesystems
ncdu /var                              # interactive, if installed

ps aux --sort=-%mem | head             # top memory hogs
ps aux --sort=-%cpu | head             # top CPU hogs
ps -ef --forest                        # process tree
top -o %MEM                            # top sorted by memory (htop if you can)
lsof -p 1234                           # files open by PID
lsof +D /var/log                       # who has files open under a dir
lsof -i :8080                          # what's listening on 8080
kill -TERM 1234 && sleep 5 && kill -0 1234 && kill -KILL 1234
tip
“Disk full” but du doesn’t add up? A deleted file is still held open by a process. lsof +L1 lists them. Restart the offender (usually a log-hoarding daemon) and the space comes back.

Network

Docs: ss(8) man page

ss -tlnp                               # listening TCP sockets + owning process
ss -s                                  # socket summary (state counts)
dig +short example.com                 # just the A record
dig example.com MX                     # specific record type
dig @1.1.1.1 example.com               # query a specific resolver
dig -x 93.184.216.34                   # reverse lookup
nc -zv host 5432                       # is that port open?
nc -l 9000                             # quick listener for testing
timeout 3 bash -c '</dev/tcp/host/443' && echo open   # no nc? pure bash
curl -sv telnet://host:6379 </dev/null # handshake check via curl
ip -br addr; ip route                  # interfaces and routing at a glance

ssh / scp / rsync

Docs: rsync(1) man page

ssh -J bastion.example.com user@10.0.2.15        # jump host
ssh -L 5432:db.internal:5432 bastion             # local port forward
ssh host 'sudo journalctl -u nginx -n 50'        # run remote command
ssh-copy-id user@host                            # install your key

scp file.tar.gz user@host:/tmp/                  # simple copy
scp -r user@host:/var/log/app ./logs/            # recursive pull

rsync -avz --progress ./site/ user@host:/var/www/site/   # sync (mind the slashes)
rsync -avz --delete src/ dst/                    # exact mirror; deletes extras
rsync -avzn --delete src/ dst/                   # -n: ALWAYS dry-run first
rsync -avz -e 'ssh -J bastion' data/ user@internal:/data/

Trailing slash on the source means “contents of”; no slash copies the directory itself.

History Tricks

Docs: History Expansion, GNU Bash manual

TrickEffect
!!Re-run the last command (sudo !! is the classic)
!$Last argument of the previous command
!*All arguments of the previous command
!grepRe-run the most recent command starting with grep
^old^newRe-run last command with first old replaced by new
Ctrl-rReverse-search history incrementally (press again to cycle)
Alt-.Insert last argument, keep pressing to go further back
$_Last argument, as a variable in scripts
mkdir -p /opt/app/releases/v2 && cd !$    # !$ expands to the new path
history | awk '{print $2}' | sort | uniq -c | sort -rn | head   # your top commands

Job Control

Docs: Job Control, GNU Bash manual

long_build.sh &                # run in background
jobs -l                        # list jobs with PIDs
fg %1                          # bring job 1 to foreground
bg %1                          # resume a Ctrl-Z'd job in background
kill %2                        # kill by job number
Ctrl-Z                         # suspend current foreground job

nohup ./migrate.sh > migrate.log 2>&1 &   # survive hangup on logout
./worker.sh & disown           # background + detach from the shell
wait                           # block until all background jobs finish
wait -n                        # until any one finishes (bash 4.3+)

For anything long-running over ssh, prefer tmux over nohup; you get your session back, output included.

0
  • Bash Basics: set -euo pipefail, parameter expansion, traps, and arrays, everything needed to turn these one-liners into scripts that survive production
  • Bash sed & awk: heavier text processing, meaning in-place edits, column sums, extracting text between markers, and the GNU vs BSD gotchas
  • tmux: session, window, and pane keybindings plus scripted layouts, so long-running pipelines outlive a dropped SSH connection

There’s more in the Bash group, and the whole cheatsheet library is one level up.

Similar Articles

More from development

Atuin Cheatsheet

Atuin cheatsheet: Ctrl-R search keys, filter and search modes, non-interactive flags, sync and …

Go (Golang) Cheatsheet

Go cheatsheet covering the go toolchain, syntax essentials, goroutines and channels, table-driven …

tmux Cheatsheet

Quick-reference tmux cheatsheet: sessions, windows, panes, copy mode, synchronize-panes, .tmux.conf …

Get new articles by email

One DevOps article a week, plus the 18-cheatsheet PDF pack. No spam, one click to leave.