Skip to content

Linux Шпаргалка

Open-source Unix-like operating system kernel and distributions.

01

Getting Started

Basic File Commands

ls -la shows all files including hidden ones. cd ~ goes home, cd - toggles to previous directory. mkdir -p creates parent directories as needed. rm -r is needed for non-empty directories.

linux
# list files
ls -la          # long format, all files
ls -lh          # human-readable sizes
ls -lt          # sort by modification time

# current directory
pwd

# change directory
cd /home/user
cd ..           # go up one level
cd ~            # go home
cd -            # go to previous directory

# create/remove directories
mkdir newdir
mkdir -p path/to/dir   # create parents
rmdir emptydir
rm -r nonempty          # remove recursively

Getting Help

man is the primary documentation system. Sections: 1=commands, 2=syscalls, 3=library, 4=devices, 5=file formats, 8=administration. man -k (or apropos) searches descriptions. --help gives a concise summary. tldr provides practical examples and is installed separately (npm install -g tldr).

linux
# manual pages
man ls                  # full documentation
man 5 crontab           # section 5 (file formats)
man -k password         # search by keyword (apropos)

# info pages (GNU tools, more detailed)
info coreutils
info ls

# quick help flags
command --help
ls --help
git --help

# tldr: community cheat sheets (install separately)
tldr tar
tldr find

# whatis: one-line description
whatis ls

# where is the binary / source / man page
whereis python3

Command History & Shortcuts

!! repeats the last command (sudo !! is the classic 'forgot sudo' fix). !N runs command N from history. !$ is the last argument. Ctrl+R is the interactive reverse search — press it again to find earlier matches. History is stored in ~/.bash_history; HISTSIZE controls in-memory size, HISTFILESIZE controls the file.

linux
# history expansion
history                 # show command history
!!                      # repeat last command
sudo !!                 # repeat last command as root
!42                     # run command #42 from history
!ls                     # run last command starting with ls
!$                      # last argument of last command
!!:gs/old/new/          # substitute in last command

# search history (interactive)
# Ctrl+R then type to search backward

# clear history
history -c              # clear current session
# edit ~/.bashrc HISTSIZE and HISTFILESIZE

# save and reload history
history -a              # append to history file
history -r              # reload from file

File Types & Identification

file inspects the magic bytes of a file to determine its true type — far more reliable than the extension. ls -F appends type indicators (* executable, / directory, @ symlink, = socket). stat shows complete inode metadata including all three timestamps: Access (atime), Modify (mtime, content), Change (ctime, metadata).

linux
# identify file type by content (not extension)
file document.pdf       # PDF document
file image.png          # PNG image data
file script.sh          # ASCII text executable
file /bin/ls            # ELF 64-bit LSB executable

# file with multiple files
file *.txt

# show MIME type
file --mime-type video.mp4   # video/mp4

# ls -F shows type indicator
ls -F
# file*       (executable)
# dir/        (directory)
# link@       (symlink)
# socket=     (socket)

# stat: detailed file info
stat file.txt
# Size, Blocks, IO Block, Device, Inode, Links,
# Access/Modify/Change/Birth times

Terminal Navigation Shortcuts

These Readline shortcuts work in bash and most shells. Ctrl+A/E jump to line start/end. Ctrl+W/U/K delete text — Ctrl+Y pastes it back (kill ring). Ctrl+R searches history. Ctrl+L clears the screen. Ctrl+S freezes output (common 'frozen terminal' cause — Ctrl+Q resumes). Learn these to dramatically speed up command-line editing.

linux
# cursor movement
Ctrl+A     # move to beginning of line
Ctrl+E     # move to end of line
Ctrl+B     # move back one char
Ctrl+F     # move forward one char

# editing
Ctrl+D     # delete char under cursor (or EOF/exit)
Ctrl+H     # delete char before cursor (backspace)
Ctrl+W     # delete word before cursor
Ctrl+U     # delete from cursor to beginning
Ctrl+K     # delete from cursor to end
Ctrl+Y     # paste (yank) deleted text back

# job control
Ctrl+C     # send SIGINT (interrupt)
Ctrl+Z     # suspend (send SIGTSTP)
Ctrl+D     # end of input / exit shell

# screen
Ctrl+L     # clear screen (same as clear)
Ctrl+S     # stop output (freeze)
Ctrl+Q     # resume output

# Alt shortcuts
Alt+B / Alt+F    # move back/forward by word
Alt+Backspace    # delete word backward

Environment Variables

Environment variables are inherited by child processes when exported. $PATH determines where the shell looks for commands — directories are searched in order separated by colons. ~/.bashrc is sourced for interactive non-login shells; ~/.profile or ~/.bash_profile for login shells. Always source the file after editing to apply changes. Use printenv VAR to check a single variable without expansion.

linux
# view all environment variables
env
printenv

# view specific variable
echo $HOME
echo $PATH
echo $USER

# set a variable (current shell only)
MYVAR="hello"
echo $MYVAR

# export to child processes
export PATH="$PATH:/opt/bin"
export EDITOR=nano

# set and export in one step
export MYVAR="hello"

# unset a variable
unset MYVAR

# common variables
# $HOME    user home directory
# $PATH    command search path
# $USER    current username
# $SHELL   current shell path
# $PWD     current working directory
# $OLDPWD  previous directory
# $PS1     prompt string

# persistent: add to ~/.bashrc or ~/.profile
echo 'export EDITOR=vim' >> ~/.bashrc
source ~/.bashrc
02

File Operations

Copying Files (cp)

cp copies files. -r (or -R) is required for directories. -i prompts before overwriting (recommended to avoid data loss). -p preserves metadata; -a is archive mode (preserves everything, including symlinks — ideal for backups). -u only updates if the source is newer. Always use -i in scripts where overwriting is risky.

linux
# copy a file
cp source.txt dest.txt

# copy to a directory
cp file.txt /backup/

# copy multiple files to a directory
cp file1.txt file2.txt /backup/

# copy directories recursively
cp -r src_dir/ dest_dir/

# interactive (prompt before overwrite)
cp -i file.txt /backup/

# preserve attributes (timestamps, permissions, ownership)
cp -p file.txt /backup/
cp -a src_dir/ dest_dir/    # archive mode (-rdp)

# only copy if source is newer
cp -u file.txt /backup/

# verbose (show what is being copied)
cp -v file.txt /backup/

# force overwrite
cp -f file.txt /backup/

Moving & Renaming (mv)

mv is both move and rename — it updates the directory entry without copying data (instant on the same filesystem). -i prompts before overwriting. mv -n prevents overwriting existing files. The rename command (Perl-based on Debian/Ubuntu) uses regex for batch renaming — much more powerful than mv loops. Note: rename syntax differs between distributions (util-linux vs perl version).

linux
# rename a file
mv oldname.txt newname.txt

# move file to another directory
mv file.txt /target/dir/

# move multiple files
mv file1.txt file2.txt /target/dir/

# interactive (prompt before overwrite)
mv -i file.txt /target/

# force overwrite
mv -f file.txt /target/

# no overwrite (don't replace existing)
mv -n file.txt /target/

# move only if newer
mv -u file.txt /target/

# verbose
mv -v olddir/ newdir/

# rename batch with rename command
rename 's/\.txt$/.md/' *.txt
rename .JPG .jpg *.JPG

Removing Files (rm)

rm permanently deletes files (no trash/recycle bin). -r is required for directories. -rf is dangerous — it deletes recursively without prompting; always double-check the path. Never run rm -rf / or rm -rf $VAR/ when $VAR might be empty. Consider trash-cli for recoverable deletion. find -delete is safer for pattern-based deletion because you can test without -delete first.

linux
# remove a file
rm file.txt

# remove multiple files
rm file1.txt file2.txt

# interactive (prompt for each file)
rm -i file.txt

# force (ignore nonexistent, no prompt)
rm -f file.txt

# remove directory recursively
rm -r directory/
rm -rf directory/        # force + recursive (DANGEROUS)

# verbose
rm -v *.tmp

# remove files matching pattern
find . -name "*.bak" -delete
find . -name "*.log" -mtime +30 -delete

# safer alternative: trash-cli
trash-put file.txt
trash-list
trash-restore

Creating Files (touch)

touch creates empty files or updates timestamps of existing files. Without options, it sets both atime and mtime to the current time. -t sets a specific timestamp. -r copies the timestamp from a reference file. To create a file with content, use redirection (>) or a here-document. touch is commonly used to create placeholder files or force make to rebuild targets.

linux
# create an empty file
touch newfile.txt

# create multiple empty files
touch file1.txt file2.txt file3.txt

# update access and modification time to now
touch existing.txt

# set specific time (YYYYMMDDHHMM format)
touch -t 202401011200 file.txt       # Jan 1, 2024 12:00

# set only access time
touch -a file.txt

# set only modification time
touch -m file.txt

# use time from another file
touch -r reference.txt target.txt

# create file with content
echo "content" > file.txt
cat > file.txt << 'EOF'
line 1
line 2
EOF

Hard & Symbolic Links (ln)

Hard links share the same inode (data) — deleting one doesn't delete the data until ALL links are removed. They can't cross filesystems or link to directories. Symlinks are path references — they break if the target moves or is deleted, but can link across filesystems and to directories. Use ln -s for symlinks (most common). readlink -f resolves the full chain to the real file.

linux
# hard link (same inode, same filesystem only)
ln original.txt hardlink.txt

# symbolic link (symlink, path reference)
ln -s /path/to/target symlink_name
ln -s original.txt softlink.txt

# force recreate a symlink
ln -sf /new/target symlink_name

# symbolic link to a directory
ln -s /var/log /home/user/logs

# view link target
readlink symlink
readlink -f symlink        # canonical absolute path
ls -l symlink              # show what it points to

# find broken symlinks
find . -type l ! -exec test -e {} \; -print

# count hard links to a file
ls -li file.txt            # 2nd column is link count

# remove a symlink
rm symlink_name
unlink symlink_name

File Metadata (stat, basename, dirname)

stat shows complete inode metadata. basename and dirname decompose paths — essential in scripts that process file lists. realpath resolves relative paths and symlinks to absolute canonical paths. stat -c allows custom output formats (%n=name, %s=size, %y=mtime, %a=octal perms) — useful in scripts. These tools replace fragile parameter expansion for path manipulation.

linux
# stat: full file information
stat file.txt
# Size: 1024  Blocks: 8  IO Block: 4096
# Device: 801h/2049d  Inode: 1234567
# Links: 1
# Access: (0644/-rw-r--r--)  Uid: 1000  Gid: 1000
# Access/Modify/Change: 2024-01-01 12:00:00

# custom format
stat -c '%n %s bytes' file.txt
stat -c '%y' file.txt            # modification time

# basename: extract filename from path
basename /var/log/syslog         # syslog
basename /var/log/syslog.log .log  # syslog

# dirname: extract directory from path
dirname /var/log/syslog          # /var/log

# realpath: resolve to absolute path
realpath ../file.txt
realpath -s symlink              # physical path (no symlink resolution)

# file size only
stat -c %s file.txt              # bytes
du -h file.txt | cut -f1         # human-readable
03

Directory Operations

Creating Directories (mkdir)

mkdir -p creates parent directories as needed and doesn't error if the directory already exists — essential for idempotent scripts. -m sets permissions directly (overrides umask). Brace expansion ({a,b,c}) combined with mkdir -p creates complex directory trees in one command. This is a common pattern for scaffolding project structures.

linux
# create a single directory
mkdir newdir

# create nested directories
mkdir -p path/to/deep/dir

# create multiple directories
mkdir dir1 dir2 dir3

# create with specific permissions
mkdir -m 755 publicdir
mkdir -m 700 privatedir

# verbose output
mkdir -v newdir

# create parent dirs with permissions
mkdir -p -m 755 /opt/myapp/{bin,lib,etc}

# create a directory tree at once
mkdir -p project/{src,tests,docs}
mkdir -p project/src/{main,utils}
mkdir -p project/{src/{main,utils},tests,docs}

Removing Directories (rmdir, rm)

rmdir only removes empty directories — safe but limited. rm -r removes non-empty directories recursively. rm -rf is the nuclear option: always verify the path first. The find -empty -delete pattern removes only empty directories without touching populated ones. In scripts, always check that the variable is non-empty before rm -rf to avoid accidentally deleting /.

linux
# remove an empty directory
rmdir emptydir

# remove directory with contents
rm -r directory/

# force remove (no prompt)
rm -rf directory/

# remove empty directories recursively (find)
find . -type d -empty -delete
find . -type d -empty -exec rmdir {} \;

# remove directory with confirmation
rm -ri directory/

# safe removal pattern in scripts
DIR="/tmp/mydir"
if [ -n "$DIR" ] && [ -d "$DIR" ]; then
    rm -rf "$DIR"
fi

# move to trash instead
trash-put directory/

Directory Listing & Tree

ls -d */ lists only directories. tree provides a visual hierarchy — -L limits depth, -I excludes patterns (incredibly useful for excluding node_modules/.git). For large directory trees, combine tree -d -L 2 for a quick overview. ls -1 (that's the number one) lists one file per line — useful for piping to wc -l for counting.

linux
# list directory contents
ls -la                    # all files, long format
ls -lh                    # human-readable sizes
ls -lt                    # sort by time (newest first)
ls -lS                    # sort by size (largest first)
ls -lr                    # reverse order
ls -R                     # recursive listing
ls -d */                  # list only directories

# tree view (install separately: apt install tree)
tree                      # show directory tree
tree -L 2                 # limit depth to 2
tree -d                   # directories only
tree -a                   # include hidden files
tree -h                   # show file sizes
tree --dirsfirst          # dirs before files
tree -I 'node_modules|.git'  # ignore patterns
tree -H . -o tree.html    # output as HTML

# count files in directory
ls -1 | wc -l
find . -maxdepth 1 -type f | wc -l

Directory Size (du)

du measures disk usage. -s (summary) shows only the total, -h makes it human-readable. du -sh */ gives sizes of immediate subdirectories. sort -rh sorts by human-readable size in reverse (largest first). --exclude skips patterns. The difference between apparent-size and default: apparent shows logical file size, default shows actual disk blocks allocated (which may be larger due to block size).

linux
# size of current directory (total)
du -sh .

# size of specific directories
du -sh /var/log /tmp /home

# size of all subdirectories
du -sh */

# sort directories by size (largest first)
du -sh * | sort -rh

# find the 10 largest directories
du -sh * | sort -rh | head -10

# include hidden files/dirs
du -sh .[!.]*

# maximum depth
du -h --max-depth=1
du -h -d 1                  # short form

# apparent size vs disk usage
du -sh --apparent-size file  # logical size
du -sh file                   # actual disk blocks

# exclude directories
du -sh --exclude='*.log' .
du -sh --exclude=node_modules .

Directory Stack (pushd, popd)

pushd/popd manage a stack of directories — pushd saves the current directory and navigates to a new one; popd returns to the saved one. dirs shows the stack. This is more powerful than cd - (which only toggles between two directories). Useful in scripts that need to navigate between several directories and return. The +N syntax rotates or removes by position.

linux
# push directory onto stack and cd into it
pushd /var/log
# pushes current dir, then cd to /var/log

# push another
pushd /tmp
# stack: /tmp /var/log /home/user

# pop back to previous directory
popd
# returns to /var/log

# list the directory stack
dirs
dirs -v           # verbose (numbered)
dirs -c           # clear the stack

# navigate by index
pushd +2          # rotate to 3rd entry
popd +1           # remove 2nd entry

# practical: save current dir, work, return
pushd /etc
cp config.conf ~/backup/
popd              # back to where we were

# alternative: use cd - for simple toggle
cd /var/log
cd -              # back to previous directory
04

File Viewing

cat & tac

cat dumps the entire file to stdout — fine for small files but use less/head for large ones. cat -n numbers lines. cat -A reveals hidden characters (tabs, line endings) — useful for debugging formatting issues. tac reverses line order (cat backwards). cat is also used to concatenate files and redirect input. For viewing large files interactively, use less instead.

linux
# display entire file
cat file.txt

# concatenate multiple files
cat file1.txt file2.txt > combined.txt

# display with line numbers
cat -n file.txt

# display with non-printing characters
cat -A file.txt           # show tabs as ^I, line ends as $
cat -v file.txt           # show non-printing chars
cat -e file.txt           # show line ends as $
cat -t file.txt           # show tabs as ^I

# append to a file
cat >> file.txt << 'EOF'
appended line
EOF

# tac: reverse line order
tac file.txt              # last line first

# create file from stdin
cat > newfile.txt
# type content, Ctrl+D to end

less Pager

less is the standard pager — far better than more because it allows backward navigation. The keybindings are vim-like (j/k/g/G). / searches forward, ? backward. F enters 'follow mode' (like tail -f) for watching logs. less automatically handles compressed files if lesspipe is configured. Press v to open the current file in your $EDITOR for quick edits.

linux
# view a file
less file.txt

# view command output
dmesg | less
ls -la /etc | less

# navigation inside less:
#   space / f     forward one page
#   b             backward one page
#   j / k         down / up one line
#   g             go to beginning
#   G             go to end
#   /pattern      search forward
#   ?pattern      search backward
#   n             next match
#   N             previous match
#   v             open in editor ($EDITOR)
#   F             follow mode (like tail -f)
#   q             quit

# useful flags
less -N file.txt          # show line numbers
less -S file.txt          # chop long lines
less -i file.txt          # case-insensitive search
less +/pattern file.txt   # open at first match

# view compressed files
less file.gz              # auto-decompresses (if lesspipe installed)

head & tail

head shows the beginning, tail shows the end of a file. tail -f is essential for monitoring log files in real-time. tail -F (capital) handles file rotation (when logs are renamed/truncated) by reopening the file. head -n -N shows everything except the last N lines. For large files, head/tail are instant (they don't read the entire file).

linux
# first 10 lines (default)
head file.txt
# last 10 lines (default)
tail file.txt

# first N lines
head -n 20 file.txt
head -20 file.txt

# last N lines
tail -n 20 file.txt
tail -20 file.txt

# all but last N lines
head -n -5 file.txt       # all except last 5 lines

# first N bytes
head -c 100 file.txt
tail -c 100 file.txt

# follow a log file (live updates)
tail -f /var/log/syslog

# follow with retry (handles log rotation)
tail -F /var/log/app.log

# show last 5 lines, then follow
tail -n 5 -f app.log

# multiple files
tail -n 5 file1.txt file2.txt

Viewing Specific Lines

sed -n 'Np' prints specific line numbers. awk with NR (record number) is more flexible for ranges and conditions. The (head; tail) trick shows both ends of a large file. nl and cat -n number lines. hexdump/xxd display binary files in hex — essential for inspecting non-text files, debugging file formats, or recovering data.

linux
# show line N (e.g., line 10)
sed -n '10p' file.txt

# show lines M to N
sed -n '10,20p' file.txt
awk 'NR>=10 && NR<=20' file.txt

# show first and last N lines
(head -5; echo "..."; tail -5) < file.txt

# show lines matching pattern
sed -n '/pattern/p' file.txt
awk '/pattern/' file.txt

# show every Nth line
awk 'NR % 3 == 0' file.txt     # every 3rd line
sed -n '0~3p' file.txt          # every 3rd line (GNU)

# number lines
nl file.txt
cat -n file.txt
grep -n "" file.txt

# show file in hex
hexdump -C file.bin
xxd file.bin
od -A x -t x1z file.bin

Word & Line Count (wc)

wc (word count) reports lines (-l), words (-w), bytes (-c), characters (-m, respects encoding), and the longest line length (-L). When given multiple files, it shows per-file counts and a total. The grep | wc -l pattern counts matching lines — but grep -c is more efficient (no pipe). For counting files, ls -1 | wc -l is quick, but find is more accurate for recursive counts.

linux
# count lines, words, and bytes
wc file.txt
#   10   50  300 file.txt
#  lines words bytes

# count lines only
wc -l file.txt

# count words only
wc -w file.txt

# count characters only
wc -m file.txt

# count bytes only
wc -c file.txt

# count the longest line length
wc -L file.txt

# count files in a directory
ls -1 | wc -l
find . -type f | wc -l

# count total lines in multiple files
wc -l *.py

# count matching lines (grep + wc)
grep "error" log.txt | wc -l

# count unique lines
sort file.txt | uniq | wc -l
07

Text Processing

sed — Stream Editor

sed edits text streams non-interactively. s substitutes (g for global); d deletes; p prints. -i edits in place (always test without -i first, or use -i.bak for a backup). The delimiter can be any character — use | or # for paths to avoid escaping slashes. -E enables extended regex with cleaner capture group syntax. sed processes line by line.

linux
# substitute (replace first occurrence per line)
sed 's/old/new/' file.txt

# substitute all occurrences (global)
sed 's/old/new/g' file.txt

# case-insensitive (GNU sed)
sed 's/old/new/gi' file.txt

# in-place editing
sed -i 's/old/new/g' file.txt
sed -i.bak 's/old/new/g' file.txt   # keep backup

# delete lines
sed '/^#/d' file.txt           # delete comment lines
sed '/^$/d' file.txt           # delete blank lines
sed '5d' file.txt              # delete line 5
sed '5,10d' file.txt           # delete lines 5-10

# print specific lines
sed -n '10,20p' file.txt       # lines 10-20
sed -n '/pattern/p' file.txt   # matching lines

# different delimiter for paths
sed 's|/usr/local|/opt|g' paths.txt

# extended regex with capture groups
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' dates.txt

# multiple commands
sed -e 's/a/A/g' -e 's/b/B/g' file.txt

awk — Column Processing

awk is a mini programming language for columnar data. $1, $2... are fields; $0 is the whole line; $NF is the last field. -F sets the input separator; OFS sets output. BEGIN runs before processing, END after. NR is the record (line) number; NF is the field count. awk is ideal for CSV/TSV processing, log analysis, and reports — far more powerful than cut.

linux
# print columns (default separator: whitespace)
awk '{print $1}' file.txt           # first column
awk '{print $1, $3}' file.txt       # columns 1 and 3
awk '{print $NF}' file.txt          # last column
awk -F: '{print $1}' /etc/passwd    # split on :

# filter and print
awk '$3 > 100' file.txt             # col 3 > 100
awk '$1 == "ERROR" {print $2}' log  # col 2 of ERROR lines
awk 'NR == 5' file.txt              # 5th line only
awk 'NR >= 10 && NR <= 20' file.txt # lines 10-20

# BEGIN and END blocks (sum, average)
awk '{sum += $1} END {print sum}' nums.txt
awk '{sum += $2; count++} END {print sum/count}' data.txt

# field separator and output
awk -F, 'BEGIN {OFS="|"} {print $1, $2}' csv.txt

# count lines matching pattern
awk '/error/ {count++} END {print count}' log

# built-in variables: NR (row), NF (fields), FS, OFS
awk '{print NR, NF, $0}' file.txt

cut & paste

cut extracts fields (-f with -d delimiter) or character positions (-c). It's fast but limited — no quoting support, so CSVs with quoted commas break it. paste merges files side by side. join combines files on a common sorted field (like SQL JOIN). expand/unexpand convert between tabs and spaces. For complex CSV processing, use awk or a dedicated tool like csvkit.

linux
# cut: extract fields by delimiter
cut -d: -f1 /etc/passwd            # field 1, delimiter :
cut -d, -f2,3 data.csv             # fields 2 and 3

# cut: extract character positions
cut -c1-5 file.txt                 # characters 1-5
cut -c1- file.txt                  # from char 1 to end

# cut: extract bytes (for multibyte)
cut -b1-10 file.txt

# paste: merge files line by line
paste file1.txt file2.txt          # tab-separated
paste -d',' file1.txt file2.txt    # comma-separated

# paste: serial (all lines of file1, then file2)
paste -s file1.txt                 # lines into one line
paste -s -d',' file1.txt file2.txt

# join: combine on a common field
join -t, -1 1 -2 1 file1.csv file2.csv

# expand/unexpand tabs
expand file.txt                    # tabs to spaces
unexpand -a file.txt               # spaces to tabs

sort & uniq

sort orders lines (-n numeric, -r reverse, -h human-readable, -k field, -t delimiter). uniq only removes ADJACENT duplicates — always pipe through sort first. uniq -c counts; sort -rn gives frequency ranking. comm compares two SORTED files: -12 shows common, -23 shows only in first, -13 shows only in second. The sort | uniq -c | sort -rn pattern is the classic frequency analysis pipeline.

linux
# sort lines
sort file.txt                      # alphabetical
sort -n nums.txt                   # numeric
sort -rn nums.txt                  # reverse numeric
sort -h sizes.txt                  # human-readable (1K, 2M, 3G)
sort -R file.txt                   # random order
sort -u file.txt                   # sort + unique

# sort by field
sort -t: -k3 -n /etc/passwd        # by 3rd field, numeric
sort -t, -k2 data.csv              # by 2nd field

# sort by multiple keys
sort -k1,1 -k2n file.txt           # key1 alpha, key2 numeric

# uniq: filter adjacent duplicates
sort file.txt | uniq               # unique lines
sort file.txt | uniq -c            # count occurrences
sort file.txt | uniq -d            # only duplicates
sort file.txt | uniq -u            # only unique lines

# top 10 most frequent lines
sort file.txt | uniq -c | sort -rn | head -10

# compare two sorted files
comm -12 <(sort a.txt) <(sort b.txt)   # common lines
comm -23 <(sort a.txt) <(sort b.txt)   # only in a
comm -13 <(sort a.txt) <(sort b.txt)   # only in b

tr — Translate Characters

tr translates or deletes characters (not regex — just character sets). -d deletes, -s squeezes repeats, -c complements (inverts the set). It's perfect for case conversion, delimiter swapping, and cleaning data. tr ',' '\n' converts CSV to one-value-per-line. tr -cd '0-9' extracts only digits. Note: tr works on characters, not strings, so it can't replace multi-character patterns — use sed for that.

linux
# translate characters
echo "Hello" | tr 'a-z' 'A-Z'       # HELLO (uppercase)
echo "Hello" | tr 'A-Z' 'a-z'       # hello (lowercase)

# swap characters
echo "abc" | tr 'abc' 'bca'         # bca

# delete characters
echo "hello" | tr -d 'l'            # heo
echo "Hello World" | tr -d ' '      # HelloWorld

# squeeze repeated characters
echo "aaabbbccc" | tr -s 'abc'      # abc
tr -s ' ' < file.txt                # squeeze repeated spaces

# complement (delete everything except)
echo "Hello123" | tr -cd '0-9'      # 123 (keep digits only)
echo "Hello123" | tr -cd 'A-Za-z'   # Hello

# convert delimiter
echo "a,b,c" | tr ',' '\n'          # a\nb\nc (one per line)
echo "a,b,c" | tr ',' '\t'          # tab-separated

# ROT13 cipher
echo "Hello" | tr 'A-Za-z' 'N-ZA-Mn-za-m'

# remove non-printable characters
tr -cd '\11\12\15\40-\176' < file.txt

tee & column

tee splits output to both a file and stdout — essential for logging within pipelines. sudo tee writes to files requiring root without running the whole pipeline as root. column -t aligns text into neat columns (great for displaying CSVs). -s specifies the input delimiter, -o the output separator. fmt and fold reflow text — fmt joins paragraphs, fold hard-wraps at a width.

linux
# tee: write to file AND stdout
echo "hello" | tee file.txt
echo "hello" | tee -a file.txt       # append

# tee to multiple files
echo "data" | tee f1.txt f2.txt f3.txt

# tee in a pipeline (log while transforming)
ls -la | tee output.txt | grep ".py"

# tee with sudo (write to privileged file)
echo "config" | sudo tee /etc/myapp.conf

# column: format as aligned columns
column -t file.txt
echo -e "a b c\n1 2 3" | column -t

# column with custom delimiter
column -t -s, data.csv
column -t -s: /etc/passwd

# column with specific separator for output
column -t -o " | " file.txt

# fmt: reformat paragraph width
fmt -w 60 file.txt

# fold: wrap lines at width
fold -w 80 file.txt
fold -w 80 -s file.txt   # break at spaces only
08

Permissions & Ownership

Viewing Permissions

Permissions are three triplets: owner, group, others. Each position is r(4), w(2), x(1). So 755 = rwxr-xr-x, 644 = rw-r--r--. For directories, x means 'can access' (cd into), r means 'can list', w means 'can create/delete files'. stat -c '%a' shows octal permissions. The first character of ls -l is the file type (- file, d dir, l symlink, b block, c char).

linux
# view file permissions
ls -l file.txt
# -rw-r--r-- 1 user group 1024 Jan 1 10:00 file.txt
#  ^^^ ^^^ ^^^
#  owner group others
#  rwx  r-x  r-x  = 755

# permission breakdown:
# - = regular file, d = directory, l = symlink
# r = read (4), w = write (2), x = execute (1)

# view all files in directory
ls -la

# view permissions recursively
ls -lR /var/www

# view in octal format
stat -c '%a %n' file.txt        # 644 file.txt
stat -c '%a %n' *

# check current umask
umask                           # e.g., 0022

# directory permissions:
# r = list contents
# w = create/delete files
# x = access (cd into) directory

chmod — Numeric Mode

Numeric (octal) notation: each digit represents owner, group, others. r=4, w=2, x=1 — sum them. 755 (rwxr-xr-x) for directories and executables; 644 (rw-r--r--) for regular files; 600 (rw-------) for private files. For web servers, the standard is dirs=755, files=644. The find-based recursive pattern applies different permissions to dirs and files — chmod -R can't do this.

linux
# numeric (octal) notation
chmod 755 script.sh        # rwxr-xr-x
chmod 644 file.txt         # rw-r--r--
chmod 600 secrets.txt      # rw------- (private)
chmod 777 publicdir/       # rwxrwxrwx (DANGEROUS)
chmod 700 ~/.ssh           # rwx------ (for SSH)

# common patterns
chmod 755 directory/       # dirs: owner full, others read+execute
chmod 644 file.txt         # files: owner read+write, others read
chmod 600 config.env       # sensitive files: owner only
chmod 666 shared.txt       # everyone read+write (no execute)
chmod 444 readonly.txt     # everyone read only

# recursive
chmod -R 755 project/
chmod -R 644 *.txt          # won't work; use find

# recursive: dirs 755, files 644 (common web server setup)
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;

# permission reference:
# 7=rwx 6=rw 5=rx 4=r 3=wx 2=w 1=x 0=none

chmod — Symbolic Mode

Symbolic notation is more readable for incremental changes: u/g/o/a (who), +/-/= (action), r/w/x/X (what). X (capital) sets execute only for directories or files that already have execute — perfect for recursive chmod. --reference copies permissions from another file. The sticky bit (+t) on a directory means only file owners can delete their files (used on /tmp).

linux
# symbolic notation
chmod u+x script.sh        # add execute for user
chmod g-w file.txt         # remove write for group
chmod o+r file.txt         # add read for others
chmod a+x script.sh        # add execute for all (a=all)

# combined changes
chmod u+x,g-w,o=r file.txt # user +x, group -w, others = read only
chmod ug+x file.txt        # add execute for user and group
chmod a=r file.txt         # set everyone to read only

# copy permissions from reference
chmod --reference=template.txt file.txt

# recursive
chmod -R u+rwX,go+rX .     # X = execute only for dirs/executables

# symbolic targets:
# u = user (owner)
# g = group
# o = others
# a = all (default)
# operators: + (add), - (remove), = (set exactly)
# permissions: r, w, x, X (execute if dir or already exec), s (SUID/SGID), t (sticky)

# set sticky bit on directory
chmod +t /tmp/shared
chmod 1777 /tmp/shared

chown & chgrp

chown changes ownership (requires root or sudo). Format: owner:group — omit the group to change only owner, omit the owner (with colon) to change only group. -R recurses into directories. chgrp is a shortcut for chown :group. --from only changes if the current owner matches (conditional). For web servers, chown -R www-data:www-data is standard. Always use -h with symlinks to change the link, not the target.

linux
# change owner
chown alice file.txt
chown alice file1.txt file2.txt

# change owner and group
chown alice:developers file.txt
chown alice:developers project/

# recursive
chown -R alice:developers project/

# change group only
chgrp developers file.txt
chown :developers file.txt        # alternative

# verbose
chown -v alice file.txt

# reference (copy ownership from another file)
chown --reference=reference.txt file.txt

# change ownership only if currently owned by specific user
chown --from=bob alice file.txt

# symbolic links (by default, changes the link target)
chown -h alice symlink            # change the link itself

# common patterns
chown -R www-data:www-data /var/www
chown root:root /etc/sudoers

# view ownership
ls -l file.txt                    # shows owner and group
stat -c '%U:%G' file.txt          # just owner:group

umask

umask sets default permissions for newly created files and directories. It's a mask — bits are removed from the default (666 for files, 777 for dirs). Common values: 022 (standard, files 644/dirs 755), 077 (private, files 600/dirs 700), 002 (group-shared, files 664/dirs 775). Set it in ~/.bashrc or /etc/profile for persistence. Note: umask can't ADD permissions, only remove them.

linux
# view current umask
umask                  # e.g., 0022
umask -S               # symbolic: u=rwx,g=rx,o=rx

# set umask
umask 022              # new files: 644, dirs: 755
umask 077              # new files: 600, dirs: 700 (private)
umask 002              # new files: 664, dirs: 775 (group-writable)

# how umask works:
# default file perms: 666 (rw-rw-rw-)
# default dir perms:  777 (rwxrwxrwx)
# umask is SUBTRACTED (mask removed)
# umask 022: file = 666-022 = 644, dir = 777-022 = 755
# umask 077: file = 666-077 = 600, dir = 777-077 = 700

# set permanently: add to ~/.bashrc
echo 'umask 022' >> ~/.bashrc

# for security-sensitive environments
umask 077              # only owner can access new files

# for shared group work
umask 002              # group members can write new files

# umask does not affect execute bit on files
# (files are created without execute by default)

Special Permissions (SUID, SGID, Sticky)

SUID (4xxx): program runs with the file owner's privileges — passwd needs this to modify /etc/shadow as root. SGID (2xxx): on executables, runs with group privileges; on directories, new files inherit the directory's group (useful for shared projects). Sticky bit (1xxx): on directories, only the file owner (or dir owner/root) can delete files — used on /tmp. Audit SUID/SGID files regularly as they're privilege escalation vectors.

linux
# SUID (Set User ID): runs as file owner
chmod u+s /usr/bin/passwd       # 4755 -> rwsr-xr-x
chmod 4755 myprogram

# SGID (Set Group ID): runs as group owner
chmod g+s /shared/dir           # dirs: new files inherit group
chmod 2755 /shared/project

# Sticky bit: only file owner can delete
chmod +t /tmp                   # 1777 -> rwxrwxrwt
chmod 1777 /shared/upload

# view special permissions
ls -l /usr/bin/sudo
# -rwsr-xr-x  (s in user position = SUID)
ls -ld /tmp
# drwxrwxrwt  (t in others position = sticky)

# find SUID files (security audit)
find / -perm -4000 -type f 2>/dev/null

# find SGID files
find / -perm -2000 -type f 2>/dev/null

# find sticky directories
find / -perm -1000 -type d 2>/dev/null

# numeric special bits:
# 1 = sticky, 2 = SGID, 4 = SUID
# chmod 4755 = SUID + 755
# chmod 2755 = SGID + 755
# chmod 1777 = sticky + 777

# uppercase S/T means execute bit is NOT set (usually an error)
# rws = SUID + execute, rwS = SUID without execute
09

Users & Groups

User Management

useradd creates users (low-level; adduser is a friendlier wrapper on Debian). -m creates the home directory; -s sets the shell; -c sets the full name; -G adds to supplementary groups. usermod -aG appends to groups without removing existing ones (always use -a with -G). userdel -r removes the home directory. passwd sets passwords. /etc/passwd stores user info; /etc/shadow stores password hashes.

linux
# add a new user
sudo useradd -m -s /bin/bash alice
# -m: create home directory
# -s: specify login shell

# add user with full options
sudo useradd -m -s /bin/bash -c "Alice Smith" -G sudo,docker alice

# set password
sudo passwd alice

# delete a user
sudo userdel alice
sudo userdel -r alice         # remove home directory too

# modify user
sudo usermod -aG docker alice   # append to group (-a = append)
sudo usermod -s /bin/zsh alice  # change shell
sudo usermod -L alice           # lock account
sudo usermod -U alice           # unlock account
sudo usermod -l newname oldname # rename user

# view user info
id alice                      # uid, gid, groups
finger alice                  # detailed info (if installed)

# change login shell
chsh -s /bin/zsh

# list all users
cat /etc/passwd | cut -d: -f1
getent passwd | cut -d: -f1

Group Management

Groups organize users for shared access. groupadd/groupdel/groupmod manage groups. usermod -aG (append to group) is the standard way to add a user — without -a, you'd replace ALL their supplementary groups. gpasswd is an alternative tool. /etc/group stores group definitions. newgrp starts a new shell with a different primary group (useful when creating files that should be group-owned).

linux
# create a group
sudo groupadd developers

# create group with specific GID
sudo groupadd -g 2000 developers

# delete a group
sudo groupdel developers

# modify group
sudo groupmod -n devs developers    # rename
sudo groupmod -g 2001 developers    # change GID

# add user to group
sudo usermod -aG docker alice       # append to group
sudo gpasswd -a alice developers    # alternative

# remove user from group
sudo gpasswd -d alice developers
sudo deluser alice developers       # Debian/Ubuntu

# list groups
cat /etc/group | cut -d: -f1
getent group

# view groups for a user
groups alice
id alice

# set group administrator
sudo gpasswd -A alice developers

# temporarily switch primary group
newgrp developers                  # new shell with group as primary

# view group members
getent group developers            # lists members
grep developers /etc/group

Password Management

passwd changes passwords. -l locks an account (adds ! to the hash); -u unlocks. -e forces a password change on next login. chage manages password aging policies: -M (max days), -m (min days), -W (warning days), -E (expiry date). Password hashes are stored in /etc/shadow (readable only by root), not /etc/passwd. Good password policy requires regular changes with complexity requirements.

linux
# change your own password
passwd

# change another user's password (requires root)
sudo passwd alice

# set password interactively
sudo passwd alice
# Enter new password: ******
# Retype new password: ******

# lock/unlock a password
sudo passwd -l alice          # lock (prepends !)
sudo passwd -u alice          # unlock

# force password change on next login
sudo passwd -e alice
sudo chage -d 0 alice

# set password aging policy
sudo chage -M 90 alice        # max 90 days
sudo chage -m 7 alice         # min 7 days between changes
sudo chage -W 7 alice         # warn 7 days before expiry
sudo chage -E 2025-12-31 alice # expire on date

# view password aging info
chage -l alice

# password quality: /etc/login.defs
# PASS_MAX_DAYS   90
# PASS_MIN_DAYS   7
# PASS_MIN_LEN    12
# PASS_WARN_AGE   7

# check password hash in /etc/shadow
sudo grep alice /etc/shadow

User Information Commands

whoami shows your current username; id shows uid/gid/groups. who lists all logged-in users; w adds what they're running. last shows login history from /var/log/wtmp; lastb shows failed attempts from /var/log/btmp. getent passwd queries the user database (including LDAP if configured). These commands are essential for system administration and auditing user activity.

linux
# who am I
whoami                       # current username
id                           # uid, gid, groups
echo $USER                   # from environment

# who is logged in
who                          # all logged-in users
w                            # detailed (what they're doing)
users                        # just usernames

# last logins
last                         # recent logins
last -n 10                   # last 10 logins
last alice                   # alice's logins
last reboot                  # reboot history

# failed login attempts
lastb                       # bad login attempts (requires root)

# user account info
finger alice                 # detailed info (if installed)
getent passwd alice          # /etc/passwd entry

# group membership
groups                       # your groups
groups alice                 # alice's groups
id alice                     # uid, gid, all groups

# check if user exists
id alice && echo "exists" || echo "not found"
getent passwd alice

# list all users with home directories
getent passwd | awk -F: '{print $1, $6}'

su & sudo

su switches users (needs target user's password); sudo runs a command as root (needs your password). su - starts a login shell (loads the target user's profile). sudo is preferred for auditing: commands are logged in /var/log/auth.log. visudo safely edits /etc/sudoers (validates syntax before saving). NOPASSWD rules are convenient but reduce security. sudo !! repeats the last command as root — the classic 'forgot sudo' fix.

linux
# switch to root
su                           # needs root password
su -                         # login shell (loads root's profile)

# switch to another user
su - alice                   # login as alice
su alice                     # as alice, keep current env

# run a single command as root
sudo command
sudo apt update

# run a command as another user
sudo -u alice command
sudo -u www-data php script.php

# edit a file as root
sudo vim /etc/fstab
sudoedit /etc/fstab          # safer alternative

# sudo with environment
sudo -E command              # preserve environment variables

# list sudo privileges
sudo -l                      # what can I run?
sudo -l -U alice             # what can alice run?

# sudo configuration
sudo visudo                  # edit /etc/sudoers safely
# /etc/sudoers.d/ for individual rules
# alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx

# run previous command as root
sudo !!                      # classic fix for forgetting sudo

# sudo timeout: 5 min by default
sudo -v                      # extend timeout
sudo -k                      # kill timeout (require password next time)
10

Process Management

ps — Process Listing

ps shows a snapshot of processes. aux (BSD) and -ef (System V) are the two common styles. The 'ps aux | grep' pattern is ubiquitous (use [n]ginx trick to avoid matching grep). pstree shows parent-child relationships. --sort=-%mem sorts by memory usage (find resource hogs). Key states: R=running, S=sleeping, D=uninterruptible sleep, Z=zombie, T=stopped. For real-time monitoring, use top or htop.

linux
# list processes (BSD style)
ps aux                       # all processes, full format
ps aux | grep nginx          # find specific process

# list processes (System V style)
ps -ef                       # all processes
ps -ef | grep python

# list processes in a tree
ps auxf                      # forest/tree view
pstree                       # dedicated tree tool
pstree -p                    # with PIDs

# show specific user's processes
ps -u alice

# show process by PID
ps -p 1234
ps -p 1234 -o pid,ppid,cmd

# custom output format
ps -eo pid,ppid,user,%mem,%cpu,cmd --sort=-%mem | head

# show threads
ps -eLf | grep java          # show threads (LWP)

# ps output columns:
# PID    process ID
# PPID   parent process ID
# USER   owner
# %CPU   CPU usage
# %MEM   memory usage
# VSZ    virtual memory size (KB)
# RSS    resident set size (KB)
# STAT   process state (R=running, S=sleeping, Z=zombie)
# START  start time
# TIME   CPU time
# CMD    command

top & htop

top is the built-in real-time process monitor. Key commands: M (sort by memory), P (sort by CPU), k (kill), q (quit). -b (batch) mode outputs text for scripting. htop is a superior alternative (colorized, scrollable, mouse support, tree view) — install with apt install htop. glances provides a comprehensive system overview. For finding what's consuming resources, top/htop sorted by %CPU or %MEM is the go-to tool.

linux
# real-time process monitor
top

# top interactive commands:
#   P       sort by CPU usage
#   M       sort by memory usage
#   N       sort by PID
#   T       sort by running time
#   k       kill a process (enter PID)
#   r       renice (change priority)
#   1       show per-CPU stats
#   u       filter by user
#   H       show threads
#   c       toggle full command path
#   W       write settings to ~/.toprc
#   q       quit

# run top with specific sorting
top -o %MEM                  # sort by memory
top -o %CPU                  # sort by CPU

# batch mode (for scripts)
top -b -n 1                  # one snapshot
top -b -n 1 | head -20       # top 20 processes

# monitor specific user
top -u alice

# htop: better interactive viewer (install separately)
htop                         # colorized, mouse support
htop -p 1234                 # monitor specific PID

# glance: alternative system monitor
glances                      # needs installation

kill & killall

kill sends signals by PID; killall/pkill send by name. Always try SIGTERM (default) first — it allows graceful cleanup. SIGKILL (-9) is forceful and immediate; the process can't catch it, so it may leave resources in a bad state. Use as a last resort. pkill -f matches the full command line. kill -l lists all signals. The 'kill; sleep; kill -9' pattern gives the process a chance to clean up before forcing.

linux
# send signal to a process by PID
kill 12345                   # SIGTERM (graceful, default)
kill -15 12345               # SIGTERM explicitly
kill -9 12345                # SIGKILL (force, cannot be caught)
kill -HUP 12345              # SIGHUP (reload config)

# kill by name
killall nginx                # kill all nginx processes
pkill -f "python app.py"     # match full command line
pkill -u alice               # kill all of alice's processes

# common signals
kill -l                      # list all signals
# SIGTERM (15)  - graceful termination (default)
# SIGKILL (9)   - force kill (cannot be caught)
# SIGINT (2)    - interrupt (Ctrl+C)
# SIGHUP (1)    - hangup (often reload config)
# SIGSTOP (19)  - pause (cannot be caught)
# SIGCONT (18)  - resume
# SIGUSR1/USR2  - user-defined signals

# graceful then forceful
kill 12345; sleep 2; kill -9 12345 2>/dev/null

# kill background job
kill %1                      # kill job 1

# find and kill by port
fuser -k 8080/tcp            # kill process using port 8080
kill $(lsof -t -i:8080)      # alternative

Background Jobs & Job Control

Appending & runs in background. jobs lists active jobs; fg/bg move between foreground and background. Ctrl+Z suspends the foreground job. wait blocks until background jobs finish. nohup and disown both prevent the process from dying when the terminal closes — nohup for new commands, disown for already-running ones. For persistent work, use tmux or screen instead.

linux
# run in background (append &)
sleep 100 &
# [1] 12345   (job 1, PID 12345)

# list jobs
jobs
jobs -l                      # with PIDs

# bring to foreground / send to background
fg                           # foreground the current job
fg %1                        # foreground job 1
bg %2                        # background job 2

# Ctrl+Z: suspend foreground job, then resume in background
# Ctrl+Z sends SIGTSTP
bg                           # resume in background

# wait for background jobs
wait                         # wait for all
wait $!                      # wait for last background job
wait 12345                   # wait for specific PID

# disown: detach from shell (survives logout)
long_task &
disown %1

# nohup: run immune to hangups (survives logout)
nohup ./script.sh > output.log 2>&1 &

# run at lower priority
nice -n 10 ./backup.sh &

# parallel execution
cmd1 & cmd2 & cmd3 &
wait                         # wait for all to finish

nice & renice — Process Priority

nice values range from -20 (highest priority) to 19 (lowest). Regular users can only lower priority (positive values); root can raise it (negative). The default is 0. nice starts a process with a priority; renice changes a running process. For I/O-intensive tasks, ionice controls disk I/O priority (classes: 1=realtime, 2=best-effort, 3=idle). cpulimit throttles CPU usage — useful for background tasks that shouldn't interfere.

linux
# nice: start a process with adjusted priority
nice ./script.sh             # default: +10 (lower priority)
nice -n 10 ./backup.sh       # priority 10 (lower)
nice -n -5 ./critical.sh     # higher priority (needs root)

# renice: change priority of running process
renice 5 -p 12345            # PID 12345 to priority 5
renice 10 -u alice           # all of alice's processes
renice -5 -p 12345           # higher priority (needs root)

# view priorities
ps -l                        # NI column shows nice value
top                          # NI column

# nice values:
#  -20  highest priority (most favorable)
#    0  default
#   19  lowest priority (least favorable)
# only root can set negative (higher) priority

# practical: run CPU-intensive task at low priority
nice -n 19 make -j4 &

# ionice: I/O priority
ionice -c 3 ./backup.sh      # idle I/O priority
ionice -c 2 -n 7 ./backup.sh # best-effort, low

# cpulimit: limit CPU usage (install separately)
cpulimit -p 12345 -l 50      # limit to 50% CPU

/proc Filesystem

/proc is a virtual filesystem (no disk storage) that exposes kernel and process information. /proc/PID/ contains per-process data. /proc/cpuinfo, /proc/meminfo show hardware. /proc/sys/ contains tunable kernel parameters (equivalent to sysctl). Writing to /proc/sys/ changes kernel settings immediately (but use sysctl for persistence). /proc/mounts shows currently mounted filesystems. This is the primary interface between userspace and the kernel.

linux
# /proc: virtual filesystem with kernel/process info
# process info by PID
cat /proc/1234/status        # process status
cat /proc/1234/cmdline       # command line (null-separated)
ls -l /proc/1234/fd/         # open file descriptors
cat /proc/1234/environ       # environment variables (null-separated)

# system info
cat /proc/cpuinfo            # CPU information
cat /proc/meminfo            # memory information
cat /proc/version            # kernel version
cat /proc/uptime             # uptime in seconds
cat /proc/loadavg            # load average

# network info
cat /proc/net/tcp            # TCP connections
cat /proc/net/dev            # network interface stats

# kernel settings
ls /proc/sys/                # sysctl parameters
cat /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/ip_forward  # enable IP forwarding

# mount info
cat /proc/mounts             # mounted filesystems
cat /proc/filesystems        # supported filesystem types

# view process memory maps
cat /proc/1234/maps          # memory mappings
pmap 1234                    # formatted version
11

System Information

uname & hostname

uname shows kernel info (-a for everything). hostname shows/sets the system name — hostnamectl set-hostname makes it persistent. /etc/os-release is the standard way to identify the distribution (works on all modern Linux). lscpu gives detailed CPU architecture info. dmidecode reads BIOS/SMBIOS data. These commands are the first step in any system administration task to understand what you're working with.

linux
# kernel and system info
uname                        # kernel name: Linux
uname -a                     # all info
uname -r                     # kernel release: 5.15.0-91-generic
uname -m                     # machine hardware: x86_64
uname -n                     # network node (hostname)
uname -p                     # processor type
uname -o                     # operating system: GNU/Linux

# hostname
hostname                     # current hostname
hostname -f                  # fully qualified domain name
hostname -I                  # all IP addresses

# set hostname (persistent)
sudo hostnamectl set-hostname myserver

# distribution info
cat /etc/os-release          # distro name and version
lsb_release -a               # Ubuntu/Debian specific
cat /etc/debian_version      # Debian version

# kernel version
cat /proc/version
dmesg | grep "Linux version"

# system architecture
arch                         # e.g., x86_64
dpkg --print-architecture    # Debian/Ubuntu

# BIOS/hardware info
sudo dmidecode -t system     # system info
sudo dmidecode -t bios       # BIOS info
lshw                         # hardware list
lscpu                        # CPU details

df — Disk Free Space

df reports filesystem disk space. -h (human-readable) is essential. -T shows the filesystem type (ext4, xfs, btrfs, tmpfs, nfs). -i shows inode usage — you can run out of inodes before running out of space (common with many small files). -x excludes filesystem types (useful to filter out virtual filesystems like tmpfs). When disk is full, check df -h first, then use du to find the space-consuming directories.

linux
# show disk space for all filesystems
df                           # all mounted filesystems
df -h                        # human-readable (K, M, G)
df -H                        # SI units (KB, MB, GB)

# specific filesystem
df -h /home                  # space for /home
df -h /dev/sda1              # specific device

# filesystem type
df -T                        # show type (ext4, xfs, tmpfs, etc.)
df -Th                       # human-readable + type

# exclude specific types
df -x tmpfs -x devtmpfs      # exclude virtual filesystems

# inodes (file count limit)
df -i                        # inode usage
df -ih                       # human-readable inode usage

# local filesystems only
df -l

# show total
df -h --total

# find the filesystem using the most space
df -h | sort -rh -k5 | head

# real-time: watch disk space
watch -n 5 df -h

du — Disk Usage

du measures disk usage. -s (summary) shows only totals; -h is human-readable. du -sh */ gives immediate subdirectory sizes. sort -rh sorts by human-readable values. --apparent-size shows logical file size vs actual disk blocks. --exclude skips patterns. For finding what's consuming disk space, the workflow is: df -h (which filesystem is full) → du -sh * (which directory) → find (which files).

linux
# size of current directory
du -sh .

# size of specific directories
du -sh /var/log /tmp /home

# size of all immediate subdirectories
du -sh */
du -sh * | sort -rh          # sorted largest first

# top 10 largest directories
du -sh * | sort -rh | head -10

# maximum depth
du -h -d 1                   # depth 1 only
du -h --max-depth=2

# include hidden files
du -sh .[!.]*

# apparent size (logical, not disk blocks)
du -sh --apparent-size file

# exclude patterns
du -sh --exclude='*.log' .
du -sh --exclude=node_modules .

# total of multiple directories
du -sh /var/log /var/lib /tmp | tail -1

# find largest files (not just directories)
find / -type f -exec ls -lhS {} + 2>/dev/null | head -10
find . -type f -printf '%s %p\n' | sort -rn | head -10

# sort all subdirectories by size
du -sh ./*/* | sort -rh | head -20

free — Memory Usage

free shows memory usage. The 'available' column is the most meaningful — it estimates how much memory is available for starting new applications without swapping. Linux intentionally uses free RAM as cache (buff/cache) to speed up disk reads; this is reclaimed on demand, so low 'free' is normal. Swap usage should be monitored — high swap usage with active swapping (si/so in vmstat) indicates memory pressure.

linux
# show memory usage
free                         # in KB
free -h                      # human-readable
free -m                      # in MB
free -g                      # in GB

# continuous update
free -h -s 1                 # update every 1 second
watch -n 1 free -h           # alternative

# output:
#               total   used   free   shared  buff/cache  available
# Mem:           16G     4G     2G     0.5G       10G          11G
# Swap:          8G      0G     8G

# detailed memory info
cat /proc/meminfo

# key fields:
# total       total RAM
# used        used by applications
# free        completely free
# buff/cache  kernel buffers and page cache
# available   estimate of memory available for new apps

# IMPORTANT: Linux uses free RAM for cache
# 'available' is more meaningful than 'free'
# Cache is reclaimed on demand, so don't panic if 'free' is low

# swap info
swapon --show                # show swap devices
cat /proc/swaps

# clear cache (requires root, use with caution)
sudo sysctl vm.drop_caches=3   # free pagecache, dentries, inodes

# process memory usage
ps aux --sort=-%mem | head -10  # top memory consumers

uptime & System Load

uptime shows how long the system has been running and the load average (1/5/15 minute averages). Load average represents the average number of processes waiting for CPU — should generally be below the number of CPU cores. vmstat shows real-time CPU, memory, and I/O stats — the 'r' column (runnable processes) and 'si/so' (swap in/out) are key indicators. iostat and sar (from sysstat package) provide detailed I/O and historical data.

linux
# system uptime and load average
uptime
# 10:30:00 up 5 days,  3:21,  2 users,  load average: 0.50, 0.35, 0.25

# load average: 1min, 5min, 15min
#   = average number of processes waiting for CPU
#   rule of thumb: should be < number of CPU cores

# check CPU count
nproc                         # number of CPU cores
lscpu | grep "^CPU(s):"

# detailed load and context switches
cat /proc/loadavg
# 0.50 0.35 0.25 2/500 12345

# vmstat: system stats over time
vmstat 1 5                    # every 1 second, 5 times
# procs --memory-- ---swap-- -----io---- -system-- ------cpu-----
# r  b   swpd   free   buff  cache  si  so    bi    bo   in   cs us sy id wa

# iostat: I/O statistics (needs sysstat package)
iostat 1                      # every 1 second
iostat -x 1                   # extended stats

# mpstat: per-CPU statistics
mpstat -P ALL 1

# sar: historical system activity
sar -u                        # CPU usage history
sar -r                        # memory usage history

# who is logged in
who
w                             # with what they're doing
12

Networking

ping & traceroute

ping tests reachability and measures latency using ICMP echo. -c limits the count (otherwise it runs forever). traceroute shows each hop to the destination — useful for diagnosing where connectivity fails. mtr combines traceroute with continuous ping (excellent for diagnosing intermittent issues). dig queries DNS records (more detailed than nslookup). nc (netcat) -z tests if a TCP port is open without sending data.

linux
# test connectivity
ping google.com
ping -c 4 google.com         # 4 packets then stop
ping -i 0.5 google.com       # 0.5s interval
ping -W 2 google.com         # 2s timeout per packet

# ping with timestamp
ping -D google.com           # unix timestamps

# check if host is reachable
ping -c 1 -W 1 host && echo "up" || echo "down"

# trace network path
traceroute google.com
traceroute -n google.com     # no DNS resolution (faster)
mtr google.com               # traceroute + ping (continuous)

# trace with TCP (bypasses ICMP blocking)
tcptraceroute google.com 443

# DNS lookup
dig example.com
dig +short example.com       # just the IP
dig MX example.com           # mail records
dig NS example.com           # name servers
dig @8.8.8.8 example.com     # specific DNS server

# alternative DNS tools
nslookup example.com
host example.com
host -t MX example.com

# check open ports
nc -zv google.com 443        # check if port 443 is open
nc -zv google.com 80 443 22  # check multiple ports

netstat & ss

ss is the modern replacement for netstat — faster and more detailed. -tlnp shows listening TCP ports with process names (needs root for -p). To find what's using a port: ss -tlnp | grep :PORT or lsof -i :PORT. ip replaces ifconfig/route (from the iproute2 package). Key: -n prevents DNS resolution (much faster). Check 'state established' for active connections. netstat is deprecated but still widely available.

linux
# ss: modern socket statistics (replaces netstat)
ss -tlnp                     # listening TCP ports + process
ss -tunap                    # all TCP/UDP connections
ss -s                        # socket summary

# netstat: classic network statistics
netstat -tlnp                # listening TCP ports
netstat -tunap               # all connections
netstat -rn                  # routing table
netstat -i                   # interface statistics

# common ss/netstat flags:
# -t  TCP
# -u  UDP
# -l  listening sockets only
# -n  numeric (no DNS resolution, faster)
# -a  all sockets
# -p  show process using socket (needs root)
# -r  routing table

# find what's listening on a port
ss -tlnp | grep :80
lsof -i :8080                # alternative

# check established connections
ss -tn state established

# show all connections to a specific host
ss -tn dst google.com

# view network interfaces
ip addr                      # modern
ip a                         # short form
ifconfig                     # legacy

# view routing table
ip route                     # modern
route -n                     # legacy

# view interface statistics
ip -s link
ifconfig eth0

curl — HTTP Client

curl is the essential HTTP client for the command line. -X sets the method; -d sends POST data; -H sets headers; -L follows redirects; -O saves with the remote filename; -s is silent (for scripting); -w '%{http_code}' extracts just the status code. For JSON APIs, combine -H 'Content-Type: application/json' with -d. -u provides basic auth. For REST API testing, curl is the universal standard.

linux
# basic GET request
curl https://api.example.com/data

# save to file
curl -O https://example.com/file.zip     # save with remote filename
curl -o file.zip https://example.com/file.zip  # custom name

# follow redirects
curl -L https://short.url/abc

# HTTP methods
curl -X POST https://api.example.com/users
curl -X PUT -d '{"name":"Alice"}' https://api.example.com/users/1
curl -X DELETE https://api.example.com/users/1

# send data
curl -d "name=Alice&age=30" https://api.example.com/form
curl -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' https://api.example.com/users

# headers
curl -H "Authorization: Bearer TOKEN" \
     -H "Accept: application/json" \
     https://api.example.com/data

# verbose / headers only
curl -v https://example.com                # verbose
curl -I https://example.com                # headers only

# authentication
curl -u user:pass https://api.example.com  # basic auth
curl --netrc-file ~/.netrc https://...     # netrc

# download with progress bar
curl --progress-bar -O https://example.com/largefile.iso

# get HTTP status code only
curl -s -o /dev/null -w "%{http_code}" https://example.com

# retry on failure
curl --retry 3 --retry-delay 5 https://example.com

# pass query parameters
curl "https://api.example.com/search?q=linux&page=2"

wget — Download Files

wget downloads files non-interactively. -c resumes interrupted downloads (essential for large files). -m mirrors websites (with -k for link conversion). -r recurses with -l for depth. -i reads URLs from a file. wget is better than curl for recursive/mirror downloads and for resuming — curl uses -C - for resume. --limit-rate throttles bandwidth. wget handles redirects and cookies automatically.

linux
# download a file
wget https://example.com/file.zip

# download with custom name
wget -O myfile.zip https://example.com/file.zip

# download in background
wget -b https://example.com/largefile.iso

# resume interrupted download
wget -c https://example.com/largefile.iso

# mirror a website
wget -m https://example.com
wget -m -k -K -E https://example.com  # convert links for local viewing

# recursive download (limited depth)
wget -r -l 2 https://example.com

# download all files of a type
wget -r -A pdf https://example.com/documents/

# continue and retry
wget -t 5 --waitretry=10 https://example.com/file.zip

# quiet mode (no output)
wget -q https://example.com/file.zip

# download from a list of URLs
wget -i urls.txt

# authentication
wget --user=user --password=pass https://example.com/protected

# set user agent
wget --user-agent="Mozilla/5.0" https://example.com

# limit download speed
wget --limit-rate=200k https://example.com/file.zip

# ignore certificate errors
wget --no-check-certificate https://example.com/file.zip

ip & ifconfig

ip (from iproute2) is the modern replacement for ifconfig/route. ip a shows addresses; ip link shows layer 2; ip route shows the routing table. Changes with ip are temporary (lost on reboot) — for persistence, edit /etc/netplan/ (Ubuntu), /etc/network/interfaces (Debian), or use nmcli (NetworkManager). ifconfig is deprecated but still widely used. For wireless, use iw/iwconfig.

linux
# view network interfaces (modern)
ip addr show                  # all interfaces
ip a                          # short form
ip a show eth0                # specific interface

# view link status
ip link show
ip -s link show eth0          # with statistics

# bring interface up/down
sudo ip link set eth0 up
sudo ip link set eth0 down

# assign IP address
sudo ip addr add 192.168.1.100/24 dev eth0
sudo ip addr del 192.168.1.100/24 dev eth0

# routing table
ip route show
ip route                      # short form
sudo ip route add default via 192.168.1.1

# view ARP table
ip neigh                      # neighbors (ARP)
arp -a                        # legacy

# DNS configuration
cat /etc/resolv.conf

# ifconfig (legacy, still common)
ifconfig                      # all interfaces
ifconfig eth0                 # specific interface
sudo ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up

# change MAC address
sudo ip link set dev eth0 address 00:11:22:33:44:55

# view wireless info
iwconfig                      # wireless interfaces
iw list                       # wireless capabilities

# network manager (desktop)
nmcli device status
nmcli connection show

DNS Lookup (dig, nslookup)

dig is the most powerful DNS lookup tool. +short gives concise output. Common record types: A (IPv4), AAAA (IPv6), MX (mail), NS (nameservers), TXT (text/SPF), CNAME (alias). @server queries a specific DNS server. -x does reverse lookups (IP to hostname). +trace shows the full resolution path from root servers. host is a simpler alternative. DNS propagation checks compare results across multiple servers.

linux
# dig: detailed DNS lookup
dig example.com
dig +short example.com        # just the IP address
dig example.com A             # A record (IPv4)
dig example.com AAAA          # AAAA record (IPv6)
dig example.com MX            # mail exchange
dig example.com NS            # name servers
dig example.com TXT           # TXT records (SPF, etc.)
dig example.com SOA           # start of authority

# query specific DNS server
dig @8.8.8.8 example.com
dig @1.1.1.1 example.com MX

# reverse DNS lookup
dig -x 1.2.3.4
dig -x 8.8.8.8 +short

# trace DNS resolution path
dig +trace example.com

# JSON output (for scripting)
dig +json example.com

# nslookup: simpler alternative
nslookup example.com
nslookup example.com 8.8.8.8   # use specific server

# host: concise output
host example.com               # IP address
host -t MX example.com        # mail records
host -t NS example.com        # name servers
host 8.8.8.8                   # reverse lookup

# check DNS propagation
# compare results from multiple servers
for dns in 8.8.8.8 1.1.1.1 9.9.9.9; do
    echo "=== $dns ==="
    dig @$dns example.com +short
done

# flush DNS cache
sudo systemd-resolve --flush-caches   # systemd
sudo resolvectl flush-caches          # newer systemd
13

Package Management

apt (Debian/Ubuntu)

apt is the user-friendly frontend to dpkg/apt-get (Debian/Ubuntu). Always run apt update before install/upgrade to refresh the package list. upgrade keeps packages; full-upgrade may remove packages to resolve dependencies. purge removes config files. autoremove cleans orphaned dependencies. dpkg -S finds which package owns a file. apt-mark hold prevents a package from being upgraded (useful for pinned versions).

linux
# update package list
sudo apt update

# upgrade installed packages
sudo apt upgrade             # upgrade without removing
sudo apt full-upgrade        # upgrade, may remove packages
sudo apt dist-upgrade        # alias for full-upgrade

# install packages
sudo apt install nginx
sudo apt install nginx=1.18* # specific version
sudo apt install -y nginx    # no prompt

# remove packages
sudo apt remove nginx        # remove but keep config
sudo apt purge nginx         # remove including config
sudo apt autoremove          # remove unused dependencies
sudo apt clean               # clean downloaded packages

# search for packages
apt search nginx
apt-cache search web server

# show package info
apt show nginx
apt-cache policy nginx       # available versions

# list installed packages
apt list --installed
dpkg -l | grep nginx

# list files installed by a package
dpkg -L nginx
dpkg -S /usr/bin/curl        # which package owns a file

# add PPA (Ubuntu)
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update

# hold package (prevent upgrade)
sudo apt-mark hold nginx
sudo apt-mark unhold nginx

dnf/yum (RHEL/Fedora/CentOS)

dnf replaces yum on modern Fedora/RHEL/CentOS 8+. The commands are similar. dnf provides (or yum provides) finds which package provides a file — essential for finding missing dependencies. rpm -ql lists files in an installed package; rpm -qf finds the owner of a file. EPEL provides additional packages for RHEL/CentOS. dnf groupinstall installs package groups (like Development Tools). yum still works as a symlink to dnf on many systems.

linux
# dnf: modern Fedora/RHEL/CentOS
sudo dnf install nginx
sudo dnf update              # update all packages
sudo dnf upgrade             # same as update
sudo dnf remove nginx

# search
dnf search nginx
dnf list available           # all available packages
dnf list installed           # installed packages

# package info
dnf info nginx
dnf provides /usr/bin/curl   # which package provides a file

# clean cache
sudo dnf clean all

# group install
dnf groupinstall "Development Tools"
dnf group list

# yum: older CentOS/RHEL (still available)
sudo yum install nginx
sudo yum update
sudo yum remove nginx
yum search nginx
yum provides /usr/bin/curl

# enable EPEL repository (Extra Packages for Enterprise Linux)
sudo dnf install epel-release

# list dependencies
dnf repoquery --requires nginx
dnf repoquery --deplist nginx

# downgrade a package
sudo dnf downgrade nginx

# view package files
rpm -ql nginx                # files installed by package
rpm -qi nginx                # package info
rpm -qf /usr/bin/curl        # which package owns file

pacman (Arch Linux)

pacman is Arch Linux's package manager. -Syu is the essential system update command (always update the full system, never partial). -S installs, -R removes, -Q queries local. -Rs removes with unused dependencies; -Rns also removes config. Arch is a rolling release — partial upgrades are unsupported and can break the system. The AUR (Arch User Repository) provides community packages via helpers like yay/paru.

linux
# update system
sudo pacman -Syu            # sync repos + upgrade all
# ALWAYS update the full system on Arch

# install packages
sudo pacman -S nginx
sudo pacman -S --needed nginx  # don't reinstall if up to date

# remove packages
sudo pacman -R nginx         # remove package only
sudo pacman -Rs nginx        # remove + unused deps
sudo pacman -Rns nginx       # remove + deps + config

# search packages
pacman -Ss nginx             # search repos
pacman -Qs nginx             # search installed

# package info
pacman -Si nginx             # info from repos
pacman -Qi nginx             # info from installed
pacman -Ql nginx             # list files
pacman -Qo /usr/bin/curl     # which package owns file

# list orphaned packages
pacman -Qdt

# clean cache
sudo pacman -Sc             # clean old packages
sudo pacman -Scc            # clean all cache

# query local database
pacman -Q                   # all installed packages
pacman -Qe                  # explicitly installed
pacman -Qm                  # foreign (AUR) packages

# AUR (Arch User Repository)
# install yay or paru for AUR support
yay -S package-name         # install from AUR

# downgrade a package
sudo pacman -U /var/cache/pacman/pkg/package-1.0-1.x86_64.pkg.tar.zst

Finding Packages

Finding packages by keyword (apt/dnf search) or by file (apt-file/dnf provides/pacman -F) is essential when you get 'command not found'. The 'which package owns this file' query (dpkg -S / rpm -qf / pacman -Qo) is invaluable for understanding what's installed. apt-file (Debian) and pacman -F (Arch) search file databases of packages not yet installed — useful for finding which package provides a needed header or library.

linux
# Debian/Ubuntu
apt search keyword
apt-cache search keyword
apt list --installed | grep keyword

# RHEL/Fedora
dnf search keyword
yum search keyword

# Arch
pacman -Ss keyword

# search by file name/path
# Debian: apt-file (separate package)
sudo apt install apt-file
sudo apt-file update
apt-file find /usr/bin/curl
apt-file search curl.h

# RHEL/Fedora
dnf provides /usr/bin/curl
dnf provides *curl.h
yum whatprovides /usr/bin/curl

# Arch
pacman -F /usr/bin/curl
pacman -Fy                  # update file database first

# check if package is installed
dpkg -l | grep nginx         # Debian
rpm -q nginx                 # RHEL
pacman -Q nginx              # Arch

# list files installed by a package
dpkg -L nginx                # Debian
rpm -ql nginx                # RHEL
pacman -Ql nginx             # Arch

# find which package owns a file
dpkg -S /usr/bin/curl         # Debian
rpm -qf /usr/bin/curl         # RHEL
pacman -Qo /usr/bin/curl      # Arch

# show package dependencies
apt-cache depends nginx      # Debian
dnf repoquery --requires nginx  # RHEL
pacman -Si nginx             # Arch

Repository Management

Repositories are package sources. On Debian/Ubuntu, PPAs (Personal Package Archives) provide third-party software — always add the signing key to verify packages. /etc/apt/sources.list.d/ holds custom repo files. On RHEL/Fedora, dnf config-manager manages repos; EPEL is essential for RHEL. On Arch, edit /etc/pacman.conf. Always verify GPG keys to prevent installing malicious packages. apt policy and dnf repolist show configured repositories.

linux
# Debian/Ubuntu: add repository
sudo add-apt-repository ppa:deadsnakes/ppa
sudo add-apt-repository "deb https://packages.example.com/ubuntu jammy main"

# remove repository
sudo add-apt-repository --remove ppa:deadsnakes/ppa

# add repository manually
echo "deb https://packages.example.com/ubuntu jammy main" | \
    sudo tee /etc/apt/sources.list.d/example.list

# add signing key (Debian)
wget -qO- https://example.com/key.gpg | \
    sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/example.gpg

# list repositories
apt policy                   # show configured repos
grep -r "" /etc/apt/sources.list.d/

# RHEL/Fedora: enable repository
sudo dnf config-manager --add-repo https://example.com/repo.repo
sudo dnf install epel-release  # enable EPEL
sudo dnf repolist              # list enabled repos
sudo dnf repolist all          # list all repos

# enable/disable a repo
sudo dnf config-manager --set-enabled epel
sudo dnf config-manager --set-disabled epel

# Arch: edit /etc/pacman.conf
# [custom]
# Server = https://example.com/$repo/os/$arch

# verify package integrity
dpkg --verify                 # Debian
rpm -Va                       # RHEL
14

Service Management (systemd)

Service Control

systemctl manages systemd services. start/stop/restart are the basic operations. reload sends a signal to re-read config without full restart (graceful). status shows current state and recent logs. mask completely prevents a service from being started (even manually) — stronger than disable. is-active and is-enabled are useful for scripts (return 0 for active/enabled).

linux
# start/stop/restart services
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx    # reload config without restart

# try-restart (only if running)
sudo systemctl try-restart nginx

# reload or restart (reload if possible, otherwise restart)
sudo systemctl reload-or-restart nginx

# send signal to service
sudo systemctl kill nginx
sudo systemctl kill -s HUP nginx

# check service status
sudo systemctl status nginx
systemctl is-active nginx       # active/inactive
systemctl is-enabled nginx      # enabled/disabled
systemctl is-failed nginx       # failed/running

# show service logs (recent)
sudo systemctl status nginx

# mask a service (prevent from starting)
sudo systemctl mask nginx
sudo systemctl unmask nginx

# reset failed state
sudo systemctl reset-failed

# emergency: reboot/poweroff
sudo systemctl reboot
sudo systemctl poweroff
sudo systemctl halt
sudo systemctl suspend

Service Status & Details

systemctl status gives a comprehensive view: state, PID, memory/CPU usage, and recent logs. systemctl cat shows the unit file (including any overrides). systemctl edit creates a drop-in override file without modifying the original — safe for customizing vendor-provided services. After editing unit files, run daemon-reload to tell systemd about the changes. list-dependencies shows what a service requires.

linux
# detailed status
systemctl status nginx
# Shows: loaded state, active state, recent log entries, cgroup info

# just the active state
systemctl is-active nginx       # "active" or "inactive"

# is it enabled at boot?
systemctl is-enabled nginx      # "enabled" or "disabled"

# check if service failed
systemctl is-failed nginx

# list dependencies
systemctl list-dependencies nginx

# show service file
systemctl cat nginx
systemctl cat nginx.service

# show service properties
systemctl show nginx
systemctl show -p ExecStart nginx

# view service file location
systemctl show -p FragmentPath nginx

# edit service file (override)
sudo systemctl edit nginx       # creates override file
sudo systemctl edit --full nginx  # edit full unit file

# reload systemd after editing unit files
sudo systemctl daemon-reload

# list sockets
systemctl list-sockets

# check all failed services
systemctl --failed

Enable/Disable Services

enable makes a service start at boot (creates symlinks); disable removes the symlinks. --now combines enable/disable with start/stop. list-unit-files shows all available services (installed but maybe not running); list-units shows loaded/active ones. The default target (multi-user = text mode, graphical = GUI) replaces traditional runlevels. Use --state= to filter by status.

linux
# enable service to start at boot
sudo systemctl enable nginx

# enable and start in one command
sudo systemctl enable --now nginx

# disable (don't start at boot)
sudo systemctl disable nginx

# disable and stop in one command
sudo systemctl disable --now nginx

# re-enable (apply symlink changes)
sudo systemctl reenable nginx

# check if enabled
systemctl is-enabled nginx

# list enabled services
systemctl list-unit-files --state=enabled

# list all service unit files
systemctl list-unit-files --type=service

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

# list all loaded services
systemctl list-units --type=service

# list services by state
systemctl list-units --type=service --state=failed
systemctl list-units --type=service --state=exited

# default boot target
systemctl get-default          # usually graphical.target or multi-user.target

# change boot target
sudo systemctl set-default multi-user.target  # text mode (runlevel 3)
sudo systemctl set-default graphical.target    # GUI mode (runlevel 5)

Listing & Filtering Services

list-units shows what's currently loaded in memory; list-unit-files shows what's installed on disk. --type filters by unit type (service, socket, timer, mount, target). --state filters by status (running, exited, failed, enabled, disabled). Timers (systemd timers) are the systemd-native replacement for cron jobs — they support dependency-based scheduling and are visible via list-timers. --reverse shows what depends on a unit.

linux
# list all active units
systemctl list-units

# list only services
systemctl list-units --type=service

# list by state
systemctl list-units --type=service --state=running
systemctl list-units --type=service --state=exited
systemctl list-units --type=service --state=failed

# list all unit files (installed)
systemctl list-unit-files --type=service

# list by state (unit files)
systemctl list-unit-files --state=enabled
systemctl list-unit-files --state=disabled
systemctl list-unit-files --state=masked

# list by target (runlevel)
systemctl list-units --type=target

# list sockets
systemctl list-units --type=socket
systemctl list-sockets

# list timers (replaces cron for many tasks)
systemctl list-timers
systemctl list-timers --all

# list mounts
systemctl list-units --type=mount

# show all units (including inactive)
systemctl list-units --all

# show specific unit type
systemctl list-units --type=service --all

# count services by state
systemctl list-units --type=service --no-legend | wc -l
systemctl list-units --type=service --state=running --no-legend | wc -l

# find services that depend on a unit
systemctl list-dependencies --reverse nginx

journalctl — System Logs

journalctl queries the systemd journal (structured logging). -f follows in real-time. -u filters by unit (service). -b filters by boot (-b -1 = previous boot). --since/--until use flexible time expressions. -p filters by priority (emerg to debug). The journal is persistent across reboots (stored in /var/log/journal/). --vacuum-time and --vacuum-size control disk usage. journalctl replaces /var/log/messages for systemd systems.

linux
# view all logs (newest last)
journalctl

# follow logs (like tail -f)
journalctl -f

# today's logs
journalctl --since today
journalctl -u nginx --since today

# specific time range
journalctl --since "2024-01-01" --until "2024-01-02"
journalctl --since "1 hour ago"
journalctl --since "30 min ago"

# logs for a specific service
journalctl -u nginx
journalctl -u nginx -f          # follow
journalctl -u nginx --since "1 hour ago"

# logs for current boot
journalctl -b                   # current boot
journalctl -b -1                # previous boot
journalctl --list-boots         # list all boots

# filter by priority
journalctl -p err               # errors only
journalctl -p warning           # warnings and above
# 0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug

# filter by process
journalctl _PID=1234
journalctl _UID=1000

# show kernel messages only
journalctl -k

# output format
journalctl -o json              # JSON format
journalctl -o short-iso         # ISO timestamps
journalctl --no-pager           # no pager (for scripts)

# clear old logs
sudo journalctl --vacuum-time=7d    # keep last 7 days
sudo journalctl --vacuum-size=100M  # keep 100MB max
15

Scheduled Tasks (Cron)

crontab Basics

crontab -e edits your scheduled tasks. The format is: minute hour day-of-month month day-of-week command. * means 'every'. */N means 'every N'. Cron runs with a minimal environment — always use absolute paths for commands and scripts. Scripts should set PATH explicitly. Output is emailed to the user (configure MAILTO in crontab) or redirect to a log file. System-wide tasks go in /etc/crontab or /etc/cron.*/ directories.

linux
# edit your crontab
crontab -e

# list your crontab entries
crontab -l

# remove all crontab entries
crontab -r

# edit another user's crontab (requires root)
sudo crontab -u alice -e
sudo crontab -u alice -l

# crontab entry format:
# minute hour day-of-month month day-of-week command
# 0-59   0-23 1-31          1-12  0-6 (0=Sunday)
#
# *  *  *  *  *  command to execute
# |  |  |  |  |
# |  |  |  |  +----- day of week (0-7) (0 or 7 is Sunday)
# |  |  |  +------- month (1-12)
# |  |  +--------- day of month (1-31)
# |  +----------- hour (0-23)
# +------------- minute (0-59)

# examples:
# run every day at 2:30 AM
30 2 * * * /home/user/backup.sh

# run every Monday at 9 AM
0 9 * * 1 /home/user/weekly-report.sh

# run every 15 minutes
*/15 * * * * /home/user/check.sh

# run at reboot
@reboot /home/user/startup.sh

# system-wide cron: /etc/crontab
# adds a user column:
# minute hour day month week USER command
*/30 * * * * root /usr/local/bin/check.sh

# cron directories (run all scripts inside)
/etc/cron.daily/    # daily
/etc/cron.hourly/   # hourly
/etc/cron.weekly/   # weekly
/etc/cron.monthly/  # monthly

Cron Schedule Syntax

Cron uses 5 fields: minute (0-59), hour (0-23), day-of-month (1-31), month (1-12), day-of-week (0-7, where 0 and 7 are Sunday). * means every; */N means every N; commas separate values; dashes define ranges. Day-of-week and day-of-month are OR'd when both are specified (not AND). The last-day-of-month trick checks if tomorrow is the 1st. Always test cron schedules with a simple echo before deploying real tasks.

linux
# every minute
* * * * * command

# every 5 minutes
*/5 * * * * command

# every hour at minute 0
0 * * * * command

# every day at midnight
0 0 * * * command

# every day at 2:30 AM
30 2 * * * command

# every Monday at 8 AM
0 8 * * 1 command

# every weekday (Mon-Fri) at 9 AM
0 9 * * 1-5 command

# every weekend (Sat and Sun) at 10 AM
0 10 * * 0,6 command

# every month on the 1st at midnight
0 0 1 * * command

# every quarter (Jan, Apr, Jul, Oct) on the 1st
0 0 1 1,4,7,10 * command

# every 15 minutes during business hours
*/15 9-17 * * 1-5 command

# twice a day (midnight and noon)
0 0,12 * * * command

# every 2 hours
0 */2 * * * command

# last day of month (tricky — use date check)
0 0 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /script.sh

# run on specific date (Jan 1 at midnight)
0 0 1 1 * command

Special Cron Strings

Special strings (@daily, @weekly, etc.) are shorthand for common schedules — more readable than the 5-field format. @reboot runs once when the system boots. MAILTO controls where output is emailed — set to empty to disable. Cron uses a minimal environment (limited PATH, no shell profile) — always set PATH explicitly or use absolute paths. Output redirection is essential: without it, output is emailed or lost.

linux
# predefined special strings (GNU cron)
@reboot     command    # run once at startup
@yearly     command    # once a year: 0 0 1 1 *
@annually   command    # same as @yearly
@monthly    command    # once a month: 0 0 1 * *
@weekly     command    # once a week: 0 0 * * 0
@daily      command    # once a day: 0 0 * * *
@midnight   command    # same as @daily
@hourly     command    # once an hour: 0 * * * *

# examples
@reboot /home/user/startup.sh
@daily /home/user/backup.sh >> /var/log/backup.log 2>&1
@hourly /usr/local/bin/health-check.sh

# set MAILTO for email notifications
MAILTO="[email protected]"
@daily /home/user/report.sh

# disable email (discard output)
MAILTO=""
@daily /home/user/cleanup.sh > /dev/null 2>&1

# environment variables in crontab
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
HOME=/home/user
@daily /home/user/script.sh

# redirect output to log
@daily /home/user/script.sh >> /home/user/cron.log 2>&1

# log with timestamp
@daily echo "$(date): starting" >> /home/user/cron.log; /home/user/script.sh >> /home/user/cron.log 2>&1

# check cron service is running
systemctl status cron
systemctl status crond

anacron

anacron is designed for machines that aren't always on (laptops, desktops). Unlike cron, it runs missed jobs when the system boots. It only supports daily/weekly/monthly granularity. The delay field adds a random wait to prevent all jobs from starting at once. systemd timers are the modern alternative — they support cron-like scheduling, can run missed jobs (Persistent=true), and integrate with journalctl for logging.

linux
# anacron: run periodic jobs even if system was off
# /etc/anacrontab format:
# period  delay  job-id  command
#   1     5      daily   /home/user/daily.sh
#   7     10     weekly  /home/user/weekly.sh
#   30    15     monthly /home/user/monthly.sh

# view anacron config
cat /etc/anacrontab

# anacron directories (system-wide)
ls /etc/cron.daily/
ls /etc/cron.weekly/
ls /etc/cron.monthly/

# run anacron manually
sudo anacron -d            # run jobs in foreground with debug
sudo anacron -f            # force run all jobs
sudo anacron -u            # update timestamps only (don't run)

# run a specific job
sudo anacron -j daily      # run the 'daily' job

# check when jobs last ran
ls -l /var/spool/anacron/

# difference between cron and anacron:
# cron:
#   - requires system to be running at scheduled time
#   - minimum resolution: 1 minute
#   - per-user crontabs
#
# anacron:
#   - catches up missed jobs after downtime
#   - minimum resolution: 1 day
#   - system-wide only (runs as root)
#   - good for laptops/desktops that aren't always on

# systemd timers: modern alternative to cron
systemctl list-timers
sudo systemctl enable --now mytask.timer

at — One-time Scheduled Tasks

at schedules one-time commands (unlike cron's recurring schedule). Times can be absolute (10:30 AM, midnight) or relative (now + 30 minutes). atq lists pending jobs; atrm removes them. batch runs when system load is below 0.8 (configurable). at jobs run with the user's environment. Requires the atd service. Useful for reminders, delayed tasks, or scheduling a shutdown without staying logged in.

linux
# schedule a command for later
echo "backup.sh" | at midnight
echo "shutdown -h now" | at 2am tomorrow

# interactive scheduling
at 2:30 PM
at> /home/user/report.sh
at> Ctrl+D                  # press Ctrl+D to submit

# schedule relative to now
at now + 30 minutes
at> echo "30 min passed" > /tmp/notice.txt
at> Ctrl+D

at now + 2 hours
at now + 1 day
at now + 1 week

# schedule for a specific time
at 10:30 AM
at 10:30 AM tomorrow
at 10:30 AM 2025-12-25
at 10:30 AM Dec 25

# list pending jobs
atq
at -l

# view a specific job
at -c 5                      # show job #5

# remove a job
atrm 5                       # remove job #5
at -d 5                      # alternative

# batch: run when load is low
batch
at> /home/user/heavy-computation.sh
at> Ctrl+D

# enable atd service
sudo systemctl enable --now atd

# check atd is running
systemctl status atd
16

Archives & Compression

tar — Create & Extract

tar bundles files into a single archive (no compression by default). The flags are mnemonic: c=create, x=eXtract, t=list, f=file, v=verbose. -C specifies the extraction directory. --exclude skips patterns. tar preserves file permissions, ownership, and timestamps. Always use tar -tf to preview contents before extracting from untrusted sources. The -f flag must be followed by the filename.

linux
# create a tar archive (no compression)
tar -cf archive.tar file1.txt file2.txt
tar -cf archive.tar directory/

# extract a tar archive
tar -xf archive.tar
tar -xf archive.tar -C /target/dir/    # extract to specific directory

# list contents without extracting
tar -tf archive.tar
tar -tvf archive.tar          # verbose (with details)

# verbose create/extract
tar -cvf archive.tar dir/     # show files being added
tar -xvf archive.tar          # show files being extracted

# append files to existing archive
tar -rf archive.tar newfile.txt

# extract specific files
tar -xf archive.tar path/to/file.txt
tar -xf archive.tar "file*.txt"

# exclude files
tar -cf archive.tar dir/ --exclude="*.log"
tar -cf archive.tar dir/ --exclude="node_modules"

# common flags:
# c = create
# x = extract
# t = list
# f = file (specify archive name)
# v = verbose
# r = append
# u = update (only newer files)
# C = change to directory

# preserve permissions (default for root)
tar -cpf archive.tar dir/     # p = preserve permissions

# show what would be done (dry run)
tar -tf archive.tar | head -10

tar with Compression

tar supports multiple compression algorithms via flags: z (gzip), j (bzip2), J (xz), --zstd (zstd). gzip is the standard (fast, everywhere); bzip2 compresses better but slower; xz is best ratio but slowest; zstd is modern (fastest, good ratio). Modern tar auto-detects compression on extraction, so -xf works for any format. Compression level (1-9) trades speed for size. For backups, xz or zstd are preferred.

linux
# tar + gzip (most common, .tar.gz or .tgz)
tar -czf archive.tar.gz dir/        # create
tar -xzf archive.tar.gz             # extract
tar -tzf archive.tar.gz             # list

# tar + bzip2 (better compression, slower)
tar -cjf archive.tar.bz2 dir/       # create
tar -xjf archive.tar.bz2            # extract
tar -tjf archive.tar.bz2            # list

# tar + xz (best compression, slowest)
tar -cJf archive.tar.xz dir/        # create
tar -xJf archive.tar.xz             # extract
tar -tJf archive.tar.xz             # list

# tar + zstd (fast + good ratio, modern)
tar --zstd -cf archive.tar.zst dir/ # create
tar --zstd -xf archive.tar.zst      # extract

# specify compression level (1-9, default 6)
GZIP=-9 tar -czf archive.tar.gz dir/    # max gzip compression
XZ_OPT=-9e tar -cJf archive.tar.xz dir/ # max xz compression

# compression comparison:
# gzip:  fast, medium compression (.tar.gz)
# bzip2: medium, good compression (.tar.bz2)
# xz:    slow, best compression (.tar.xz)
# zstd:  fastest, good compression (.tar.zst)

# extract: tar auto-detects compression
tar -xf archive.tar.gz    # works without -z flag
tar -xf archive.tar.xz    # works without -J flag

# compress to stdout (for piping)
tar -cz dir/ | ssh user@host "cat > backup.tar.gz"

zip & unzip

zip is common on Windows and widely supported. -r recurses directories; -e encrypts with a password (prompted); -x excludes patterns; -s splits into multiple parts (useful for large files). unzip -l lists; -d extracts to a directory; -t tests integrity. Unlike tar+gzip, zip compresses each file individually, allowing random access to files within the archive. For cross-platform sharing, zip is the safest choice.

linux
# create a zip archive
zip archive.zip file1.txt file2.txt

# zip a directory recursively
zip -r archive.zip directory/

# zip with password (encryption)
zip -e -r archive.zip directory/
zip -P password archive.zip file.txt   # password on command line (insecure)

# zip with specific compression level (0-9)
zip -9 -r archive.zip directory/       # maximum compression

# zip excluding files
zip -r archive.zip dir/ -x "*.log" -x "*/node_modules/*"

# split archive into parts
zip -s 100m archive.zip largefile.iso   # 100MB parts

# update/add files to existing zip
zip archive.zip newfile.txt

# list contents
unzip -l archive.zip
unzip -v archive.zip            # verbose with details

# extract
unzip archive.zip
unzip archive.zip -d /target/dir/

# extract specific files
unzip archive.zip file1.txt

# test archive integrity
unzip -t archive.zip

# extract to stdout
unzip -p archive.zip file.txt

# zipinfo: detailed listing
zipinfo archive.zip
zipinfo -1 archive.zip          # just filenames

gzip & gunzip

gzip compresses single files (not archives — use tar for multiple files). It replaces the original with a .gz file; use -k to keep it. -c outputs to stdout (useful for piping). zcat/zless read compressed files without extracting. gzip only handles one file at a time, so tar + gzip is the standard for directories. Compression level 9 gives the smallest file but takes longer; level 1 is fastest.

linux
# compress a single file (replaces original)
gzip file.txt                # creates file.txt.gz, removes file.txt

# keep the original file
gzip -k file.txt             # keep original
gzip -c file.txt > file.txt.gz  # alternative (to stdout)

# set compression level (1-9, default 6)
gzip -1 file.txt             # fastest, least compression
gzip -9 file.txt             # slowest, best compression

# decompress
gunzip file.txt.gz           # creates file.txt, removes .gz
gzip -d file.txt.gz          # same as gunzip
gunzip -c file.txt.gz > file.txt  # keep original
gunzip -k file.txt.gz        # keep original (GNU)

# compress multiple files (each gets its own .gz)
gzip file1.txt file2.txt file3.txt

# recursive (compress all files in directory)
gzip -r directory/

# test integrity
gzip -t file.txt.gz

# list compression info
gzip -l file.txt.gz
# compressed  uncompressed  ratio  uncompressed_name
#       1234        5678    78.3%  file.txt

# zcat: view compressed file without extracting
zcat file.txt.gz
zcat file.txt.gz | grep "error"

# zless: pager for compressed files
zless file.txt.gz

# concatenate and decompress
cat file1.txt.gz file2.txt.gz | gunzip > combined.txt

# compress stdin
echo "data" | gzip > data.gz
tar -cf - dir/ | gzip > archive.tar.gz

xz & bzip2

bzip2 compresses better than gzip but slower. xz (LZMA) offers the best compression ratio but is the slowest — ideal for archiving. zstd is the modern choice: fastest decompression with good ratio. lz4 is extremely fast but lower ratio. For distributing software, xz is preferred (smallest download). For real-time compression, zstd or lz4. bzcat/xzcat/zcat read compressed files without extracting — useful for log analysis.

linux
# bzip2: better compression than gzip
bzip2 file.txt               # compress (replaces original)
bzip2 -k file.txt            # keep original
bunzip2 file.txt.bz2         # decompress
bzip2 -d file.txt.bz2        # same as bunzip2

# bzip2 compression level (1-9, default 9)
bzip2 -9 file.txt

# bzcat: view bzip2-compressed file
bzcat file.txt.bz2 | grep "pattern"

# xz: best compression ratio
xz file.txt                  # compress (replaces original)
xz -k file.txt               # keep original
xz -d file.txt.xz            # decompress
unxz file.txt.xz             # same as xz -d

# xz compression level (0-9, default 6)
xz -9 file.txt               # maximum compression

# xz with extreme compression (very slow)
xz -9e file.txt

# xzcat: view xz-compressed file
xzcat file.txt.xz

# zstd: fast modern compressor
zstd file.txt                # compress
zstd -d file.txt.zst         # decompress
zstd -19 file.txt            # max compression
zstd -k file.txt             # keep original

# lz4: extremely fast
lz4 file.txt                 # compress
lz4 -d file.txt.lz4          # decompress

# compare compression ratios
ls -lh file.txt file.txt.gz file.txt.bz2 file.txt.xz

# benchmark compression
time gzip -9 file.txt
time xz -9 file.txt
17

SSH & Remote Access

SSH Connection

ssh connects to remote machines securely. -p specifies a non-default port; -i selects a private key. ssh-copy-id installs your public key for passwordless authentication (much more secure than passwords). -v helps diagnose connection issues (key negotiation, authentication steps). -X enables X11 forwarding for remote GUI apps. For frequent connections, configure ~/.ssh/config with aliases.

linux
# basic connection
ssh user@hostname
ssh [email protected]
ssh [email protected]

# specify port (default 22)
ssh -p 2222 user@host

# use a specific private key
ssh -i ~/.ssh/id_ed25519 user@host

# run a single command and exit
ssh user@host "uname -a"
ssh user@host "ls -la /var/log"

# verbose (debug connection issues)
ssh -v user@host
ssh -vv user@host            # more verbose
ssh -vvv user@host           # most verbose

# forward X11 (GUI applications)
ssh -X user@host
ssh -Y user@host             # trusted X11 forwarding

# compression (slow connections)
ssh -C user@host

# keep alive
ssh -o ServerAliveInterval=60 user@host

# force SSH version
ssh -1 user@host             # SSH protocol 1 (insecure, avoid)
ssh -2 user@host             # SSH protocol 2 (default, secure)

# copy SSH key to remote (passwordless login)
ssh-copy-id user@host
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
ssh-copy-id -p 2222 user@host

# test connection without running commands
ssh -T user@host

SSH Key Management

Ed25519 keys are recommended (smaller, faster, more secure than RSA). The private key must be chmod 600 or SSH refuses it. ssh-agent caches passphrases in memory so you type it once per session; ssh-add -l lists loaded keys. ssh-keygen -R removes a host from known_hosts (useful after a server reinstall). Always verify host fingerprints out-of-band to prevent man-in-the-middle attacks. Never share or commit your private key.

linux
# generate an SSH key pair (Ed25519, recommended)
ssh-keygen -t ed25519 -C "[email protected]"

# generate RSA key (4096 bits, if Ed25519 not supported)
ssh-keygen -t rsa -b 4096 -C "[email protected]"

# generate with custom filename
ssh-keygen -t ed25519 -f ~/.ssh/id_server -C "server key"

# generate without passphrase (automated scripts)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/deploy_key

# copy public key to remote server
ssh-copy-id user@host

# view public key
cat ~/.ssh/id_ed25519.pub

# view fingerprint
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

# key files:
# ~/.ssh/id_ed25519       private key (keep secret! chmod 600)
# ~/.ssh/id_ed25519.pub   public key (can share)

# authorized_keys on remote server
# add your public key to: ~/.ssh/authorized_keys
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# SSH agent (manages keys in memory)
eval "$(ssh-agent -s)"        # start agent
ssh-add ~/.ssh/id_ed25519     # add key to agent
ssh-add -l                   # list loaded keys
ssh-add -D                   # remove all keys from agent

# remove or change a key passphrase
ssh-keygen -p -f ~/.ssh/id_ed25519

# verify host key fingerprint (avoid MITM)
ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub

# known_hosts management
ssh-keygen -R 192.168.1.100   # remove a host entry
ssh-keygen -F 192.168.1.100   # find a host entry

# key permissions (critical!)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

SCP — Secure Copy

scp uses SSH for encrypted file transfer. -r for directories, -P (capital!) for port (ssh uses lowercase -p). For large or frequent transfers, prefer rsync over scp — rsync resumes interrupted transfers and only sends differences. scp is being deprecated in favor of sftp in newer OpenSSH versions, but remains widely available and simple for quick copies.

linux
# copy local file to remote
scp file.txt user@host:/path/to/dest/
scp file.txt user@host:~/        # home directory

# copy remote file to local
scp user@host:/var/log/syslog ./
scp -r user@host:/var/log /tmp/  # recursive (directory)

# copy between two remote hosts
scp user1@host1:/file user2@host2:/dest

# specify port
scp -P 2222 file.txt user@host:/dest

# preserve attributes (timestamps, perms)
scp -p file.txt user@host:/dest

# compress during transfer
scp -C bigfile.tar user@host:/dest

# limit bandwidth (KB/s)
scp -l 1000 file user@host:/dest    # ~1 MB/s

SSH Config File

~/.ssh/config lets you create aliases for hosts, avoiding long ssh commands with many flags. Just 'ssh dev' connects with all the right settings. Wildcards (*.internal) apply to matching hosts. This file must be chmod 600. IdentitiesOnly yes prevents SSH from trying every key in ~/.ssh (which can cause 'too many authentication failures'). This is the single biggest SSH quality-of-life improvement.

linux
# ~/.ssh/config - simplify connections
Host dev
    HostName dev.example.com
    User alice
    Port 2222
    IdentityFile ~/.ssh/id_dev

Host prod
    HostName prod.example.com
    User deploy
    IdentityFile ~/.ssh/id_deploy
    ServerAliveInterval 60

# wildcard: all hosts in a domain
Host *.internal
    User admin
    ForwardAgent yes

# use: ssh dev  (instead of ssh -p 2222 [email protected])
#      scp file prod:/tmp/

# common useful options
Host *
    ServerAliveInterval 60      # keep connection alive
    ServerAliveCountMax 3       # disconnect after 3 missed
    AddKeysToAgent yes          # auto-add keys to agent
    IdentitiesOnly yes          # only use specified keys

Port Forwarding & Tunnels

Port forwarding tunnels traffic through SSH's encrypted channel. -L (local) exposes a remote service on your machine; -R (remote) does the reverse (useful for reaching a NAT'd machine); -D creates a SOCKS proxy. -fN runs in background without a shell. ProxyJump (-J) is the modern way to hop through bastion hosts — much cleaner than nested SSH. These are essential for accessing internal services securely.

linux
# local forward: access remote service locally
# local:8080 -> remote:80
ssh -L 8080:localhost:80 user@host
# then open http://localhost:8080 in browser

# local forward to a third host via jump server
ssh -L 8080:internal.db:3306 user@jumphost

# remote forward: expose local service to remote
# remote:8080 -> local:80
ssh -R 8080:localhost:80 user@host

# dynamic forward (SOCKS proxy)
ssh -D 1080 user@host
# then configure browser to use SOCKS5 proxy at localhost:1080

# run in background (no command, no terminal)
ssh -fN -L 8080:localhost:80 user@host

# jump host / proxy jump (OpenSSH 7.3+)
ssh -J user@jumphost [email protected]
# or in ~/.ssh/config:
#   Host internal
#       ProxyJump jumphost

# forward ports in config file
Host db
    HostName db.internal
    LocalForward 3306 localhost:3306
18

Disk Management

Listing Block Devices

lsblk is the modern way to list disks and partitions (replacing fdisk -l for viewing). df shows filesystem disk space (-h human-readable, -i for inodes — if inodes are full, you can't create files even with free space). du measures directory sizes; the 'sort -rh | head' pattern finds space hogs. Run du with --max-depth to limit recursion depth.

linux
# list block devices (modern, recommended)
lsblk                         # tree view
lsblk -f                      # with filesystem info
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT
lsblk -d                      # don't print partitions

# old way: list disks and partitions
sudo fdisk -l
sudo fdisk -l /dev/sda        # specific disk

# view partition table
sudo parted -l
sudo parted /dev/sda print

# filesystem disk space
df -h                         # human-readable
df -hT                        # with filesystem type
df -i                         # inode usage
df -h /                       # specific mount

# directory space usage
du -sh /path                  # summary
du -sh /*                     # top-level dirs
du -h --max-depth=1 /var      # one level deep
du -ah /var | sort -rh | head -10   # 10 largest

Mounting & Unmounting

mount attaches filesystems; umount detaches them. If 'target is busy', use -l (lazy) to detach when no longer in use. /etc/fstab defines mounts that happen at boot — always test with 'mount -a' after editing before rebooting! The UUID= form is more reliable than /dev/sdX (which can change on reboot). findmnt gives a cleaner view than mount.

linux
# mount a filesystem
sudo mount /dev/sdb1 /mnt/data
sudo mount -t ext4 /dev/sdb1 /mnt/data

# mount with options
sudo mount -o ro /dev/sdb1 /mnt/data          # read-only
sudo mount -o noexec /dev/sdb1 /mnt/data      # no executables
sudo mount -o uid=1000,gid=1000 /dev/sdb1 /mnt  # set owner

# mount an ISO image
sudo mount -o loop ubuntu.iso /mnt/iso

# unmount
sudo umount /mnt/data
sudo umount /dev/sdb1

# lazy unmount (busy filesystem)
sudo umount -l /mnt/data

# force unmount
sudo umount -f /mnt/data

# list mounted filesystems
mount | column -t
findmnt                        # cleaner tree view

# /etc/fstab - persistent mounts
# <device>  <mount>  <type>  <options>  <dump>  <pass>
# /dev/sdb1  /data    ext4   defaults   0       2

Creating & Formatting Partitions

Use GPT (gpt label) for disks over 2TB; MBR (msdos) is the legacy default. ext4 is the safe Linux default; XFS is common on RHEL/CentOS; Btrfs offers snapshots and compression. ALWAYS double-check the device name before mkfs — it destroys all data! fsck checks/repairs filesystems (run on unmounted filesystems for safety). parted is non-interactive and scriptable, unlike fdisk.

linux
# interactive partition editor
sudo fdisk /dev/sdb
#   m = help, n = new, p = print, d = delete, w = write, q = quit

# GPT partitioning (for disks > 2TB)
sudo parted /dev/sdb mklabel gpt
sudo parted /dev/sdb mkpart primary ext4 0% 100%

# MBR partitioning
sudo parted /dev/sdb mklabel msdos
sudo parted /dev/sdb mkpart primary ext4 1MiB 100%

# format filesystem
sudo mkfs.ext4 /dev/sdb1           # ext4 (Linux default)
sudo mkfs.xfs /dev/sdb1            # XFS (RHEL default)
sudo mkfs.btrfs /dev/sdb1          # Btrfs
sudo mkfs.ntfs /dev/sdb1           # NTFS (Windows)
sudo mkfs.vfat /dev/sdb1           # FAT32

# label a filesystem
sudo e2label /dev/sdb1 mydata      # ext4
sudo xfs_admin -L mydata /dev/sdb1 # xfs

# check filesystem for errors
sudo fsck /dev/sdb1
sudo fsck -y /dev/sdb1             # auto-repair

Logical Volume Management (LVM)

LVM abstracts physical disks into flexible logical volumes. Key advantage: you can resize volumes and span multiple disks. The hierarchy is PV (physical) -> VG (volume group, a pool) -> LV (logical volume, what you format/mount). Resizing is online for ext4 (resize2fs) and XFS (xfs_growfs, grow only). Snapshots enable consistent backups. LVM is standard on RHEL/Fedora.

linux
# LVM layers: PV -> VG -> LV
# physical volume (disk/partition)
sudo pvcreate /dev/sdb /dev/sdc
sudo pvdisplay
sudo pvs                        # summary

# volume group (pool of PVs)
sudo vgcreate myvg /dev/sdb /dev/sdc
sudo vgdisplay
sudo vgs

# logical volume (carved from VG)
sudo lvcreate -L 50G -n mylv myvg
sudo lvcreate -l 100%FREE -n mylv myvg   # use all space
sudo lvdisplay
sudo lvs

# use the LV (path: /dev/<vg>/<lv>)
sudo mkfs.ext4 /dev/myvg/mylv
sudo mount /dev/myvg/mylv /mnt/data

# resize LV (ext4)
sudo lvextend -L +10G /dev/myvg/mylv
sudo resize2fs /dev/myvg/mylv    # grow filesystem

# resize LV (xfs)
sudo lvextend -L +10G /dev/myvg/mylv
sudo xfs_growfs /mnt/data

# snapshot (point-in-time copy)
sudo lvcreate -L 5G -s -n mysnap /dev/myvg/mylv

Swap & Memory

Swap extends physical memory using disk space. Swap files (created with fallocate/mkswap) are simpler than swap partitions and equally performant on modern kernels. swappiness (0-100) controls how aggressively the kernel swaps — lower values keep more in RAM (good for databases). free -h is the quick memory summary; /proc/meminfo has detailed breakdown. vmstat 1 shows ongoing memory/cpu/io stats.

linux
# show swap usage
swapon --show
free -h

# create a swap file (modern method)
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# make permanent (add to /etc/fstab)
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# remove swap file
sudo swapoff /swapfile
sudo rm /swapfile

# adjust swappiness (0-100, lower = less swap use)
cat /proc/sys/vm/swappiness       # default 60
sudo sysctl vm.swappiness=10      # runtime
# permanent: add to /etc/sysctl.conf

# memory info
free -h                           # summary
cat /proc/meminfo                 # detailed
vmstat 1                          # continuous
19

Pipes & Redirection

Standard Streams & Redirection

Every process has three streams: stdin (0), stdout (1), stderr (2). > redirects stdout (>> appends); 2> redirects stderr. 2>&1 merges stderr into stdout's destination (order matters — put it last). &> is the Bash shortcut for >file 2>&1. Here-docs (<<EOF) feed multi-line input; quoting the delimiter ('EOF') disables variable expansion. /dev/null is the black hole for discarding output.

linux
# three standard streams:
#   stdin  (0) - input
#   stdout (1) - normal output
#   stderr (2) - error output

# redirect stdout to file (overwrite)
echo "hello" > file.txt

# append stdout to file
echo "world" >> file.txt

# redirect stderr to file
ls /nonexistent 2> errors.log

# redirect both stdout and stderr
ls / /nonexistent > all.log 2>&1
ls / /nonexistent &> all.log        # bash shortcut

# discard output (send to /dev/null)
command > /dev/null 2>&1

# redirect stdin from file
sort < unsorted.txt
wc -l < file.txt

# here-string (feed string to stdin)
grep "error" <<< "some error text"

# here-document (multi-line input)
cat << EOF
Line one
Line two with $HOME expanded
EOF

# quoted here-doc (no expansion)
cat << 'EOF'
$HOME stays literal
EOF

Pipes & Pipelines

Pipes (|) connect one command's stdout to another's stdin, building powerful pipelines. tee splits output to both a file and stdout (great for logging while viewing). Process substitution <(cmd) treats a command's output as a temporary file — essential for commands like diff and comm that expect file arguments. By default a pipeline's exit status is the last command's; set -o pipefail makes it fail if any stage fails.

linux
# pipe: connect stdout to stdin
command1 | command2

# count files
ls | wc -l

# find and kill
ps aux | grep nginx | grep -v grep

# filter and sort
cat access.log | grep "404" | sort | uniq -c | sort -rn | head

# chain transformations
cat data.csv | cut -d, -f2 | sort | uniq -c | sort -rn

# tee: write to file AND stdout
command | tee output.log
command | tee -a output.log        # append

# pipe stderr too
command 2>&1 | grep error

# pipefail: catch failures in pipeline
set -o pipefail                    # pipeline fails if any command fails

# process substitution (command as file)
diff <(ls dir1) <(ls dir2)
comm <(sort file1) <(sort file2)
while read line; do echo "$line"; done < <(grep pattern file)

xargs & Parallel Execution

xargs converts stdin into command arguments — essential for piping find output to commands like rm, cp, or grep. ALWAYS use -0 with find -print0 to handle filenames with spaces/newlines. -I {} lets you place the argument anywhere in the command. -P N runs N commands in parallel. GNU parallel is a more powerful alternative (progress bars, remote execution, retry). Never run 'xargs rm' without testing first — use -t to preview or -p to confirm.

linux
# xargs: build arguments from stdin
echo "a b c" | xargs mkdir

# one argument per line
find . -name "*.bak" | xargs rm

# handle spaces in filenames (null-delimited)
find . -name "*.log" -print0 | xargs -0 rm

# limit arguments per command
find . -name "*.txt" | xargs -n 1 cp -t /dest/

# show commands before running
find . -name "*.bak" | xargs -t rm

# prompt before each command
find . -name "*.bak" | xargs -p rm

# replace string with argument
find . -name "*.jpg" | xargs -I {} convert {} {}.png

# parallel execution (GNU parallel is more powerful)
find . -name "*.png" | xargs -P 4 -I {} convert {} {}.jpg   # 4 parallel

# GNU parallel (if installed)
parallel -j 4 convert {} {.}.jpg ::: *.png
parallel gzip ::: *.log

Command Substitution & Grouping

$() captures a command's output (preferred over backticks — nests cleanly, no escaping needed). { } groups commands in the current shell (note the spaces and final ;); ( ) runs in a subshell (changes like cd don't affect the parent). && and || are short-circuit operators — 'A && B || C' mimics a ternary but is buggy if B can fail. Use these for concise one-liners, but prefer if/else for robust scripts.

linux
# command substitution: use output as argument
files=$(ls *.txt)
today=$(date +%Y-%m-%d)
echo "Today is $today"

# backtick form (legacy, avoid)
files=`ls *.txt`

# arithmetic expansion
result=$(( 5 * 3 ))
count=$(( count + 1 ))

# group commands with { } (runs in current shell)
{ cd /tmp; ls; pwd; } > output.txt

# subshell with ( ) (runs in child process)
(cd /tmp && make clean && make)    # doesn't change your cwd

# sequential execution
cmd1 ; cmd2            # run cmd2 regardless of cmd1
cmd1 && cmd2           # run cmd2 only if cmd1 succeeds
cmd1 || cmd2           # run cmd2 only if cmd1 fails

# background
cmd &                  # run in background
cmd1 & cmd2 &          # both in parallel

# conditional pipeline
grep -q "error" log.txt && echo "found" || echo "not found"

Useful Pipeline Patterns

These patterns combine pipes, redirection, and text tools into powerful one-liners. The 'sort | uniq -c | sort -rn | head' pattern is the universal frequency analyzer. awk + sort + uniq is the standard log-analysis toolkit. Process substitution <(sort ...) feeds sorted input to join/comm. Always quote special chars in tr patterns. These pipelines embody the Unix philosophy: small tools combined to solve complex problems.

linux
# top 10 largest files
du -ah . | sort -rh | head -10

# find most frequent log entries
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

# extract and count HTTP status codes
awk '{print $9}' access.log | sort | uniq -c | sort -rn

# find files modified in last 24h, grouped by size
find . -mtime -1 -type f -exec du -h {} + | sort -rh

# unique IPs that hit /admin
grep "/admin" access.log | awk '{print $1}' | sort -u

# word frequency in a text file
tr -s ' ' '\n' < file.txt | sort | uniq -c | sort -rn | head

# join CSV files on first field
join -t, <(sort -t, -k1 file1.csv) <(sort -t, -k1 file2.csv)

# parallel downloads
cat urls.txt | xargs -n 1 -P 4 wget -q

# find which process uses the most memory
ps aux | sort -nk 4 | tail -5

# count lines matching pattern across files
grep -c "ERROR" *.log | awk -F: '{sum+=$2} END {print sum}'

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.