"double"
| variables</code>and<code>(commands) expand; the text stays one argument
| echo "HOME"</code>prints<code>/home/you</code></p></td></tr><tr><tdcolspan="1"rowspan="1"><p>none</p></td><tdcolspan="1"rowspan="1"><p>Theshellsplitsonspacesandexpands<code>∗</code></p></td><tdcolspan="1"rowspan="1"><p><code>echo∗</code>listsfiles</p></td></tr></tbody></table><p>Putregexpatternsinsinglequotessotheshelldoesn′tinterpret<code>(</code>,<code>∣</code>,<code>∗</code>or<code> first: grep -E 'exec|system' file. A backslash escapes one character: \; is a literal semicolon, which is why find ... -exec cmd {} \; has it.
Expansion Symbol | Meaning |
|---|
VAR</code>/<code>{VAR}
| Value of a variable (USER</code>,<code>HOME, PATH</code>)</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>(cmd) | Run cmd and use its output in place. cmd (backticks) is the old form of the same thing | $?
| Exit status of the last command (0 = success) | *
| Any characters: ls *.php | ?
| Exactly one character | [abc]
| One of those characters | {a,b,c}
| Makes variants: cp file{,.bak} becomes cp file file.bak | ~
| Your home directory | !!
| The previous command: sudo !! reruns it with sudo | #
| Starts a comment |
3. Loops, conditions, scriptsfor loop: repeat commands once per item in a list.
for u in alice bob carol; do
echo "checking $u"
id "$u"
done
$u is the current item. The list can come from a command, as in the cron audit I gave you:
for u in $(cut -d: -f1 /etc/passwd); do sudo crontab -l -u "$u" 2>/dev/null; done
This reads as: take the first field of every line in /etc/passwd, and for each username run crontab -l. while read: go through a file or command output line by line.
cut -d: -f1 /etc/passwd | while read -r name; do echo "user: $name"; done
if and tests:
if [ -f /etc/ssh/sshd_config ]; then echo "exists"; else echo "missing"; fi
Keep spaces inside [ ]. Common tests: -f is a file, -d a directory, -e exists, -z string is empty, -n string is not empty, -eq numbers equal, == strings equal. Script skeleton: #!/bin/bash
# audit.sh - read-only checks
echo "== listeners =="; ss -tulpn
echo "== uid 0 =="; awk -F: '$3==0' /etc/passwd
Make it runnable with chmod +x audit.sh, then run it with ./audit.sh. $1, $2 are the arguments you pass to the script. Pasting a file through the console: cat > audit.sh <<'EOF'
...paste script...
EOF
The quotes around EOF stop the shell from expanding </code>signsinwhatyoupaste.</p><h3id="18500c1f−5e73−4e70−8392−beef010b26cb"data−toc−id="18500c1f−5e73−4e70−8392−beef010b26cb"collapsed="false"seolevelmigrated="true">4.Searchingandprocessingtext</h3><p><strong>grep</strong>findslinesmatchingapattern.</p><tablestyle="min−width:50px;"><colgroup><colstyle="min−width:25px;"><colstyle="min−width:25px;"></colgroup><tbody><tr><thcolspan="1"rowspan="1"><p>Flag</p></th><thcolspan="1"rowspan="1"><p>Meaning</p></th></tr><tr><tdcolspan="1"rowspan="1"><p><code>−i</code></p></td><tdcolspan="1"rowspan="1"><p>Ignorecase</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−v</code></p></td><tdcolspan="1"rowspan="1"><p>Invert:linesthatdon′tmatch</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−r</code></p></td><tdcolspan="1"rowspan="1"><p>Searchfoldersrecursively</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−n</code></p></td><tdcolspan="1"rowspan="1"><p>Showlinenumbers</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−l</code></p></td><tdcolspan="1"rowspan="1"><p>Printonlythenamesoffilesthatmatch</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−c</code></p></td><tdcolspan="1"rowspan="1"><p>Countmatchinglines</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−w</code></p></td><tdcolspan="1"rowspan="1"><p>Matchwholewordsonly</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−o</code></p></td><tdcolspan="1"rowspan="1"><p>Printonlythematchingpart</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−E</code></p></td><tdcolspan="1"rowspan="1"><p>Extendedregex,so<code>∣</code>,<code>+</code>,<code>?</code>and<code>()</code>work</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−F</code></p></td><tdcolspan="1"rowspan="1"><p>Treatthepatternasplaintext,notregex</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−I</code></p></td><tdcolspan="1"rowspan="1"><p>Skipbinaryfiles</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−A3</code>/<code>−B3</code>/<code>−C3</code></p></td><tdcolspan="1"rowspan="1"><p>Show3linesafter,before,oraroundeachmatch</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>−−include=′∗.php′</code></p></td><tdcolspan="1"rowspan="1"><p>Onlysearchmatchingfilenames</p></td></tr></tbody></table><p>Regexbasics:<code>.</code>isanycharacter,<code>∗</code>iszeroormoreoftheprevious,<code></code>isstartofline,<code> is end of line, [abc] is one of these, \s is whitespace, and (a|b) is a or b (needs -E). awk works on columns: awk -F: '{print $1}' /etc/passwd # -F: splits fields on ":"; $1 is the first field
awk -F: '$3==0' /etc/passwd # only lines where field 3 equals 0
awk -F: '7! /nologin/' /etc/passwd # !~ means "does not match"
$0 is the whole line and NF is the number of fields.
sed edits streams: sed 's/old/new/g' file replaces every occurrence. sed -n '5,10p' file prints lines 5 to 10. sed '/pattern/d' file deletes matching lines. sed -i edits the file itself (-i.bak keeps a backup first). Other text tools Command | What it does |
|---|
cut -d: -f1
| Take field 1 using : as the separator | sort (-n numeric, -r reverse, -u unique)
| Sort lines | uniq -c
| Count repeated lines (input must be sorted first) | wc -l
| Count lines (-w words) | head -n 5 / tail -n 5
| First or last 5 lines. tail -f follows a file live | tr 'a-z' 'A-Z'
| Translate or delete characters | xargs
| Turn incoming lines into arguments: find . -name '*.bak' | xargs ls -l | diff -r dirA dirB
| Compare folders (-u unified, -q just say which files differ) | less file
| Page through a file: /text searches, n next, q quits | column -t
| Line up columns neatly | watch -n 5 'cmd'
| Rerun a command every 5 seconds |
A pipeline example. Count failed SSH logins by source IP: grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head
Read it left to right: find failed lines, take the IP column ($(NF-3) is the 4th field from the end), sort, count repeats, sort by count descending, show the top 10. 5. findStructure: find WHERE TESTS ACTION. Part | Meaning |
|---|
-name 'x' / -iname
| Match name (case-sensitive / insensitive) | -type f / d / l
| File / directory / symlink | -perm -4000
| Has at least the SUID bit set (/4000 means any of the bits) | -user bob
| Owned by bob | -mmin -60
| Modified within the last 60 minutes (+60 is older than that) | -mtime -1
| Modified within the last day | -newer file
| Modified more recently than that file | -size +1M
| Larger than 1 MB | -xdev
| Don't cross into other filesystems | -maxdepth 2
| Go at most 2 levels deep | -o / ! / \( \)
| OR / NOT / grouping | -print / -ls
| Print names / print detailed listing | -exec cmd {} \;
| Run cmd once per file ({} is the file) | -exec cmd {} +
| Run cmd once with many files at a time | -delete
| Delete matches. Run the same find without it first |
6. Files, permissions, usersFiles Command | Notes |
|---|
ls -lah
| Long listing, hidden files, readable sizes (-t newest first) | cp -a src dst
| Copy preserving owner, permissions, timestamps | rm -r dir
| Delete a folder. rm -rf also skips all questions, so double-check the path | mkdir -p a/b/c
| Create nested folders | cat, tac, file, stat
| Print a file, print it reversed, identify its type, show full metadata | strings file
| Print readable text inside a binary | sha256sum file
| Fingerprint a file. sha256sum -c list.txt verifies a list | du -sh dir / df -h
| Size of a folder / free disk space | tar -czf out.tgz dir
| create, gzip, to file. -xzf extracts, -tzf lists. -C dir changes where | lsattr / chattr +i file
| Show or set special attributes. +i immutable, -i removes it, +a append-only |
Reading ls -l output: -rwxr-xr-x 1 root root ... file. The first character is the type (- file, d directory, l link). Then three groups of rwx for owner, group and everyone else. Numbers add up: r=4, w=2, x=1, so 755 is rwxr-xr-x. Command | Notes |
|---|
chmod 600 f / chmod u+x f
| Set permissions numerically or symbolically. -R recurses | chown user:group f
| Change owner and group (-R recurses) | Special bits | 4000 = SUID (runs as the file's owner), 2000 = SGID, 1000 = sticky
|
Users and groups Command | Notes |
|---|
id [user], whoami, groups
| Who you are and which groups you're in | who -a, w, last -a, lastb
| Current logins, what they're doing, login history, failed logins (lastb needs sudo) | getent passwd
| List users. Fields: name:x:UID:GID:comment:home:shell | useradd -m -s /bin/bash name
| Create user (-m home dir, -s shell) | usermod -L name / -U
| Lock / unlock account. -aG group adds to a group (the -a means append) | usermod -s /usr/sbin/nologin name
| Block interactive login | passwd -l name / -S name
| Lock / show status | userdel -r name
| Delete user and home dir | gpasswd -d user group
| Remove from a group | sudo -l
| What can I run with sudo? sudo -u bob cmd runs as bob | sudo -i / su - name
| Root shell / login shell as another user | visudo -c
| Check sudoers syntax without editing |
7. Processes and networkProcesses Command | Notes |
|---|
ps aux
| a all users' processes, u user-oriented columns, x include ones with no terminal. No dash is the old BSD style
| ps -ef
| -e every process, -f full format. Does mostly the same job with the other style
| ps -eo pid,ppid,user,cmd --forest
| Pick your own columns; --forest draws parent/child tree | pgrep -a name
| Find PIDs by name | kill PID / kill -9 PID
| Ask politely (SIGTERM) / force kill (SIGKILL) | pkill -f text
| Kill by matching the whole command line. Check what it matches first | lsof -i :80
| Which process has port 80 open | ls -l /proc/PID/exe
| The real binary a process is running (cwd, cmdline are there too) | htop / top
| Live view. htop has F9 to kill | tmux
| Keeps sessions alive: tmux new -s x, detach Ctrl-b d, tmux attach -t x, split Ctrl-b % |
Network Command | Notes |
|---|
ss -tulpn
| -t TCP, -u UDP, -l listening, -p process, -n numbers. -s summary
| ss -tn state established
| Current connections | ip -br a / ip r
| Short address list / routing table | curl -I url
| Headers only (-s silent, -k ignore bad cert, -v verbose) | nc -zv host 80
| Test if a port is open (-z just probe, -v verbose) | ping -c 4 host
| Send 4 pings | tcpdump -nn -i eth0 port 22
| Capture traffic (-nn don't resolve names) | ssh -p 2222 user@host / scp -r dir user@host:/path
| Custom port / copy a folder over SSH |
8. Services, logs, packages, firewallsystemctl and journalctl Command | Notes |
|---|
systemctl status X
| State plus recent log lines | systemctl start/stop/restart/reload X
| Reload re-reads config without dropping connections | systemctl enable/disable X
| Start at boot or not. --now also starts or stops it immediately | systemctl mask X
| Prevent it from being started at all | systemctl cat X
| Show the unit file (look at ExecStart) | systemctl list-units --type=service --state=running
| What's running | systemctl list-unit-files --state=enabled
| What starts at boot | journalctl -u X
| Logs for one service. -e jump to end, -x add explanations, -f follow live, -n 50 last 50, -b since boot, -p err errors only, --since "1 hour ago" |
Useful logs on Ubuntu: /var/log/auth.log (SSH logins, sudo), /var/log/syslog, and web server logs under /var/log/apache2/ or /var/log/nginx/ (access.log, error.log). Packages (apt/dpkg) Command | Notes |
|---|
apt update
| Refresh the package list | apt install --reinstall pkg
| Restore a package's original files | apt remove pkg / apt purge pkg
| Remove / remove plus config files | dpkg -l / dpkg -S /path
| List installed / which package owns this file | debsums -c
| Show files that differ from the package originals | which X / type X
| Where a command comes from |
ufw Command | Notes |
|---|
ufw status verbose / status numbered
| Show rules (numbered for deleting) | ufw allow 22/tcp
| Allow a port and protocol | ufw delete 3
| Delete rule number 3 | ufw default deny incoming
| Set the default policy | ufw allow from 10.0.0.5 to any port 22
| Allow one source only | ufw limit ssh
| Allow, but rate-limit repeated attempts | ufw show added
| Show the rules you added, before enabling |
9. Habits that prevent disastersPreview destructive commands. Put echo in front of a loop's rm, or run find without -exec/-delete, and read the output before running it for real. Test config before reloading. sshd -t && systemctl reload ssh. Keep a second session open when touching SSH or the firewall. Shortcuts. Tab completes, Ctrl-r searches history, Ctrl-c stops a command, Ctrl-d exits, Ctrl-l clears the screen. Looking things up. man -k word searches manual pages by keyword, cmd --help is shorter than man, and explainshell.com breaks a pasted command into its parts.
Want me to turn this into a printable cheat sheet or a Google/Word doc? I could also build a set of practice exercises that use these commands on a fake "compromised" box. |