Linux systems 32 entries 24 run in the sandbox

Linux files and navigation cheat sheet

Move around the filesystem, inspect files and find what changed. Each entry has a worked example and the flags worth knowing, and where one is easy to get wrong, the gotcha.

Inspect files

cat -n <file>

Print a file with line numbers, handy when a config error reports a line.

cat -n notes.txt
1	This is a sample notes file.
     2	Learn Linux commands step by step.
-n
Number every output line.
-A
Show tabs as ^I and line ends as $, exposing trailing spaces and CRLF endings.
-s
Squeeze repeated blank lines into one.

Gotcha A config that looks right but fails to parse often has Windows line endings. cat -A shows them as ^M$.

head -n <N> <file>

Print the first N lines of a file or stream (10 by default).

head -n 5 /etc/passwd
-n N
Print N lines; -n -N prints everything except the last N.
-c N
Print the first N bytes.

tail -n <N> <file>

Print the last N lines of a file, where the newest log lines live.

tail -n 20 /var/log/syslog
-n N
Print the last N lines; -n +N starts at line N.
-c N
Print the last N bytes.

tail -F <file>

Follow a file as it grows, and keep following across log rotation.

tail -F /var/log/nginx/error.log
-f
Follow the open file descriptor as new data is appended.
-F
Follow by name and retry, so it survives logrotate replacing the file.
--pid=PID
Stop following when process PID exits.

Gotcha tail -f keeps reading the old, rotated file forever. Use -F on anything logrotate manages.

less <file>

Page through a large file without loading it all into memory.

less +G /var/log/syslog
+G
Open at the end of the file.
+F
Start in follow mode like tail -f; Ctrl-C stops following.
/pattern
Search forward; n and N jump to the next or previous match.
-S
Chop long lines instead of wrapping them.

tac <file>

Print a file with its lines in reverse order, so the newest log entries come first.

tac notes.txt

wc -l <file>

Count lines, words or bytes. Pipe into it to count matches.

grep ' 500 ' access.log | wc -l
-l
Count newline characters (lines).
-w
Count words.
-c
Count bytes.
-m
Count characters, respecting multibyte encodings.

Gotcha wc -l counts newlines, so a last line without a trailing newline is not counted.

stat <file>

Show a file's inode metadata: size, mode, owner, and access, modify and change times.

stat -c '%a %U:%G %s %n' /etc/nginx/nginx.conf
644 root:root 2412 /etc/nginx/nginx.conf
-c FORMAT
Custom output: %a octal mode, %U owner, %G group, %s size, %y mtime.
-L
Follow symlinks and describe the target.

file <path>

Identify a file's type from its contents rather than its extension.

file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, dynamically linked
-i
Print the MIME type and charset instead.
-L
Follow symlinks.

hexdump -C <path>

Show the bytes of a file next to the characters they print as. It finds what an editor hides: a carriage return, a tab, a byte order mark, binary data.

printf 'key: value\r\n' | hexdump -C
00000000  6b 65 79 3a 20 76 61 6c  75 65 0d 0a              |key: value..|
0000000c
-n 64
Only the first 64 bytes.
xxd
The same in another layout; xxd -p prints the hex digits alone.
od -c
Each byte as a character or its escape: \r, \t, \0.

Gotcha 0d 0a at the end of a line is a Windows line ending. A script saved with those fails with "bad interpreter" or "command not found"; sed -i 's/\r$//' removes them.

diff -u <old> <new>

Compare two files and print a unified diff, the same format code review tools use.

diff -u nginx.conf.bak nginx.conf
-u
Unified format with three lines of context.
-r
Recurse into directories.
-q
Only report whether files differ.
-w
Ignore all whitespace changes.

Create, copy & move

mkdir -p <dir>

Create a directory and any missing parents. Succeeds quietly if it already exists.

mkdir -p projects/api/logs
-p
Create parent directories as needed; no error if present.
-m MODE
Set the mode of the new directory, for example -m 750.
-v
Print each directory created.

touch <file>

Create an empty file, or update the timestamps of an existing one.

touch deploy.lock
-c
Do not create the file if it does not exist.
-d DATE
Use DATE instead of now, for example -d '2 hours ago'.
-m
Change only the modification time.

echo <text> > <file>

Write text to a file. > truncates and replaces; >> appends to the end.

echo "maintenance until 14:00" >> notes.txt
>
Redirect stdout, truncating the file first.
>>
Redirect stdout, appending.
2>&1
Send stderr to wherever stdout currently points.

Gotcha One > instead of >> wipes the file. set -o noclobber makes > refuse to overwrite existing files.

<cmd> | sudo tee -a <file>

Write a stream to a file and to stdout. With sudo, it is the correct way to append to a root-owned file.

echo "10.0.0.5 api.internal" | sudo tee -a /etc/hosts
-a
Append instead of overwriting.
> /dev/null
Add after tee to hide the echo on your terminal.

Gotcha sudo echo text > /etc/hosts fails with permission denied: the redirect is performed by your unprivileged shell, not by sudo.

cp -a <src> <dest>

Copy files or trees while preserving mode, ownership, timestamps and symlinks.

cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
-a
Archive mode: recursive and preserves everything it can.
-r
Recurse into directories (does not preserve metadata).
-i
Ask before overwriting.
-v
Print each file copied.

Gotcha Take a .bak copy before editing a live config. It turns a bad edit into a one-command rollback.

mv <src> <dest>

Move or rename a file. Within one filesystem this is an atomic rename.

mv app.conf.new app.conf
-i
Ask before overwriting.
-n
Never overwrite an existing file.
-v
Explain what is being done.

Gotcha Write a new config to a temp file in the same directory and mv it into place, so readers never see a half-written file.

rm <file>

Delete files. There is no trash can; removal is immediate.

rm build.lock
-r
Remove directories and their contents recursively.
-f
Ignore missing files and never prompt.
-i
Prompt before every removal.
-I
Prompt once before removing more than three files or recursing.

Gotcha rm -rf "$DIR"/* with an empty $DIR expands to /*. Use ${DIR:?} so the shell aborts when the variable is unset.

ln -s <target> <link>

Create a symbolic link that points at another path.

ln -sfn /opt/app/releases/42 /opt/app/current
-s
Create a symbolic link instead of a hard link.
-f
Replace an existing link.
-n
Treat an existing link to a directory as a file, so -f replaces it instead of creating a link inside it.

Gotcha Target first, link name second, the same order as cp. Switching a current symlink is a common zero-downtime release trick.

Find files

find <dir> -name <pattern>

Walk a directory tree and print paths that match name, type, age or size tests.

find /var/log -name "*.log" -mtime +7
-name / -iname
Match the base name with a glob; -iname ignores case.
-type f|d|l
Regular files, directories or symlinks.
-mtime +N
Modified more than N days ago (-N means within N days).
-size +100M
Larger than 100 MiB.
-maxdepth N
Descend at most N levels.

Gotcha Quote the pattern. Unquoted *.log is expanded by your shell before find ever sees it.

find <dir> -mmin -<N>

List files changed in the last N minutes. The first question in many incidents: what changed?

find /etc -type f -mmin -60
-mmin -N
Modified less than N minutes ago.
-newer FILE
Modified more recently than FILE.
-cmin -N
Metadata (mode, owner) changed less than N minutes ago.

find <dir> -xdev -size +<N>

Find the largest files on one filesystem when a disk is filling up.

find / -xdev -type f -size +500M
-xdev
Stay on the starting filesystem; do not cross mount points.
-size +N[kMG]
Larger than N kibibytes, mebibytes or gibibytes.
-printf '%s %p\n'
Print size and path for sorting (GNU find).

find <dir> ... -delete

Delete every file that matches the tests. Always run the same command without -delete first.

find /tmp -type f -name "*.tmp" -mtime +3 -delete
-delete
Remove each match; implies -depth.
-exec cmd {} +
Run cmd with many matches per invocation.
-print0
Null-separate output for xargs -0.

Gotcha Put -delete last. find evaluates left to right, so find . -delete -name '*.tmp' deletes everything before the name test runs.

<list> | xargs <cmd>

Turn lines of input into arguments for another command.

find . -name "*.bak" -print0 | xargs -0 rm -v
-0
Input items are null-separated (pair with find -print0).
-n N
Pass at most N arguments per command.
-P N
Run up to N commands in parallel.
-I {}
Replace {} in the command with each input item.

Gotcha Without -0, a file name with a space becomes two arguments and xargs acts on the wrong paths.

Shell productivity

history

List the commands you ran in this shell. Pipe it to grep to find the one that worked last time.

history | grep systemctl
!!
Re-run the previous command, as in sudo !!.
!$
The last argument of the previous command.
Ctrl-R
Reverse incremental search through history.
history -c
Clear this session's history list.

Gotcha A leading space keeps a command out of history when HISTCONTROL includes ignorespace. Use it before typing anything secret, then rotate the secret anyway.

watch -n <sec> <cmd>

Re-run a command every few seconds and redraw the output. Good for watching a recovery happen.

watch -n 2 'ss -tln'
-n SEC
Interval between runs (default 2 seconds).
-d
Highlight what changed since the last run.
-g
Exit when the output changes.

Gotcha Quote the command when it has a pipe or a variable; otherwise your own shell runs that part once, before watch starts.

Your pager is ready.

Free, instant, and it works on your phone. No signup: start as a guest and save your progress later.