Linux Commands Plinko

Since you're probably on Ubuntu, you can drop the Fedora parts: use ufw, apt/dpkg/debsums, the SSH service is called ssh, and logins are logged in /var/log/auth.log. I can't watch the playlist, so if some of this repeats what you've already covered, skim past it.

1. How a command is built

command  -flags  arguments
ss       -tulpn
grep     -rIn   "exec"  /var/www
  • Flags (options) change how the command behaves. Single-letter flags with one dash can be glued together: ss -tulpn is the same as ss -t -u -l -p -n. Long flags use two dashes (--all, --recursive) and can't be glued.

  • Some flags take a value: head -n 5, cut -d: -f1, ssh -p 2222. The value must come right after the flag.

  • Arguments are what the command acts on: files, directories, names, patterns.

  • The same letter means different things in different commands. Don't memorize letters, check the command's --help.

Flag

Examples of what it means

-a

ls -a all files including hidden; who -a all info; cp -a archive (copy everything, preserve permissions); ps -a all users' processes with a terminal

-r / -R

recursive (go into subfolders): grep -r, rm -r, cp -r, chmod -R

-f

rm -f force; tail -f follow live; ps -f full format; tar -f the file name

-v

grep -v invert (show lines that DON'T match); cp -v verbose

-i

grep -i ignore case; rm -i ask first; sed -i edit file in place

-n

grep -n show line numbers; head -n 5 number of lines; ss -n don't resolve names

-p

ss -p show process; mkdir -p make parent dirs; cp -p preserve; ssh -p port

-l

ls -l long listing; grep -l only file names; ss -l listening; wc -l lines

-h

human-readable sizes: ls -lh, df -h, du -h

-a versus |: -a is a flag that belongs to one command. | is a shell symbol that sits between two commands (see below). They aren't related.

2. Shell symbols

Pipe and redirection

Symbol

Meaning

Example

|

Send the output of the left command into the right one

ps aux | grep ssh

>

Write output to a file, overwriting it

ls > out.txt

>>

Append output to the end of a file

date >> log.txt

<

Feed a file in as input

sort < names.txt

2>

Redirect errors (stream 2)

find / 2> errors.txt

2>/dev/null

Throw errors away (/dev/null is a black hole)

find / -perm -4000 2>/dev/null

2>&1

Send errors to the same place as normal output

cmd > all.txt 2>&1

| tee file

Show output AND save it (-a appends)

ss -tulpn | tee ports.txt

Correction to my earlier plan: sudo ss -tulpn > /root/base-ports.txt fails for a normal user. Your shell does the >, not sudo, so it tries to write to /root without root rights. Use this instead:

sudo ss -tulpn | sudo tee /root/base-ports.txt > /dev/null

The same applies to the ps -ef and getent passwd lines in that step. Or run sudo -i once to get a root shell and then run them normally.

Chaining commands

Symbol

Meaning

;

Run the next command regardless: cd /tmp; ls

&&

Run the next one only if the first succeeded: sshd -t && systemctl reload ssh

||

Run the next one only if the first failed: cmd || echo failed

&

Run in the background: sleep 60 &

\ at end of line

Continue the command on the next line

&& is good practice for risky changes. The config test above stops the reload if the test fails.

Quotes

Style

Behavior

Example

'single'

Everything is literal, nothing expands

echo 'HOME′</code>prints<code>HOME'</code> prints <code>HOME

"double"

variables</code>and<code>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>HOME"</code> prints <code>/home/you</code></p></td></tr><tr><td colspan="1" rowspan="1"><p>none</p></td><td colspan="1" rowspan="1"><p>The shell splits on spaces and expands <code>*</code></p></td><td colspan="1" rowspan="1"><p><code>echo *</code> lists files</p></td></tr></tbody></table><p>Put regex patterns in single quotes so the shell doesn't interpret <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</code> / <code>{VAR}

Value of a variable (USER</code>,<code>USER</code>, <code>HOME, PATH</code>)</p></td></tr><tr><tdcolspan="1"rowspan="1"><p><code>PATH</code>)</p></td></tr><tr><td colspan="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, scripts

for 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></code> signs in what you paste.</p><h3 id="18500c1f-5e73-4e70-8392-beef010b26cb" data-toc-id="18500c1f-5e73-4e70-8392-beef010b26cb" collapsed="false" seolevelmigrated="true">4. Searching and processing text</h3><p><strong>grep</strong> finds lines matching a pattern.</p><table style="min-width: 50px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Flag</p></th><th colspan="1" rowspan="1"><p>Meaning</p></th></tr><tr><td colspan="1" rowspan="1"><p><code>-i</code></p></td><td colspan="1" rowspan="1"><p>Ignore case</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-v</code></p></td><td colspan="1" rowspan="1"><p>Invert: lines that don't match</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-r</code></p></td><td colspan="1" rowspan="1"><p>Search folders recursively</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-n</code></p></td><td colspan="1" rowspan="1"><p>Show line numbers</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-l</code></p></td><td colspan="1" rowspan="1"><p>Print only the names of files that match</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-c</code></p></td><td colspan="1" rowspan="1"><p>Count matching lines</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-w</code></p></td><td colspan="1" rowspan="1"><p>Match whole words only</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-o</code></p></td><td colspan="1" rowspan="1"><p>Print only the matching part</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-E</code></p></td><td colspan="1" rowspan="1"><p>Extended regex, so <code>|</code>, <code>+</code>, <code>?</code> and <code>()</code> work</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-F</code></p></td><td colspan="1" rowspan="1"><p>Treat the pattern as plain text, not regex</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-I</code></p></td><td colspan="1" rowspan="1"><p>Skip binary files</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>-A 3</code> / <code>-B 3</code> / <code>-C 3</code></p></td><td colspan="1" rowspan="1"><p>Show 3 lines after, before, or around each match</p></td></tr><tr><td colspan="1" rowspan="1"><p><code>--include='*.php'</code></p></td><td colspan="1" rowspan="1"><p>Only search matching file names</p></td></tr></tbody></table><p>Regex basics: <code>.</code> is any character, <code>*</code> is zero or more of the previous, <code>^</code> is start of line, <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! /nologin7 !~ /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. find

Structure: 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, users

Files

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 network

Processes

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, firewall

systemctl 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 disasters

  • Preview 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.