Skip to main content
WebsiteGitHub last commitGitHub commit activityGitHub IssuesDocker PullsDiscordLocalized

Helpful Commands for Ubuntu

Helpful Commands for Ubuntu

A reference for commonly used Ubuntu terminal commands, written for beginners. Each command includes an explanation of what it does and what any flags mean.

What is a flag?

Flags (also called options) are extra instructions you add to a command, always starting with - or --. For example, in ls -l, the -l flag tells ls to show a detailed (long) list instead of just filenames.


System Information

View OS version

lsb_release -a
  • lsb_release prints information about the Linux distribution.
  • -a means all — show every available detail (distributor, description, release number, codename).

View kernel version

uname -r
  • uname prints system information.
  • -r means release — show only the kernel version number (e.g. 6.8.0-45-generic).

View system uptime

uptime

Prints how long the system has been running, how many users are logged in, and the average CPU load over the last 1, 5, and 15 minutes. No flags needed.


View CPU info

lscpu

Lists detailed information about your CPU: architecture, number of cores, threads, speed, and more. No flags needed.


View memory (RAM) usage

free -h
  • free shows how much RAM and swap space is used vs. available.
  • -h means human-readable — displays sizes in MB/GB instead of raw bytes (e.g. 3.8G instead of 3981234).

View disk usage

df -h
  • df stands for disk free — shows how much space is used and available on each mounted filesystem.
  • -h means human-readable — displays sizes in MB/GB instead of raw bytes.

Package Management (APT)

APT is Ubuntu's built-in package manager. Use it to install, update, and remove software.

Update package lists

sudo apt update

Downloads the latest list of available packages from Ubuntu's software repositories. This does not install or upgrade anything — it only refreshes the list so Ubuntu knows what's available.

Before installing or upgrading

Always run apt update first so Ubuntu knows what's available.


Upgrade installed packages

sudo apt upgrade -y

Installs the newest versions of all currently installed packages.

  • -y means yes — automatically answers "yes" to any confirmation prompts so you don't have to type it manually.

Install a package

sudo apt install <package-name>

Downloads and installs the named package. Replace <package-name> with the actual name, e.g. sudo apt install curl.


Remove a package

sudo apt remove <package-name>

Uninstalls the named package but keeps its configuration files on disk (useful if you want to reinstall later and keep your settings).


Remove a package and its config files

sudo apt purge <package-name>

Uninstalls the named package and deletes its configuration files. Use this when you want a completely clean removal.


Remove unused dependencies

sudo apt autoremove -y

Removes packages that were installed automatically to support other software but are no longer needed.

  • -y automatically confirms the removal.

Search for a package

apt search <keyword>

Searches the package list for packages matching the keyword. No sudo needed since this only reads data.


Show package details

apt show <package-name>

Displays information about a package: version, description, dependencies, and more. No sudo needed.


File & Directory

List files

ls -lah
  • ls lists the contents of a directory.
  • -l means long format — shows permissions, owner, size, and modification date for each file.
  • -a means all — includes hidden files (files whose names start with .).
  • -h means human-readable — shows file sizes in KB/MB/GB instead of bytes.

You can combine these flags: -lah is the same as -l -a -h.


Change directory

cd /path/to/directory

Moves your terminal session into the specified directory. Replace /path/to/directory with the actual path, e.g. cd /home/john/Documents.

ShortcutMeaning
cd ~Go to your home directory
cd ..Go up one level
cd -Go back to the previous directory

Create a directory

mkdir -p /path/to/directory
  • mkdir creates a new directory.
  • -p means parents — also creates any missing parent directories in the path. Without -p, the command fails if the parent directory doesn't exist.

Example: mkdir -p /home/john/projects/myapp creates all three folders at once if they don't exist.


Create an empty file

touch /path/to/file
  • Creates the file if it does not exist.
  • If the file already exists, touch updates its access and modification timestamps without changing its contents.

Useful for creating placeholder files or resetting timestamps for testing.


echo "hello world"

Prints the string to standard output (your terminal).

Redirect to a file with > (overwrite) or >> (append):

# overwrite the file with new content
echo "config=true" > /path/to/file.txt

# append a line without erasing existing content
echo "another line" >> /path/to/file.txt
> overwrites without warning

> silently replaces the entire file. Use >> when you want to add to an existing file.


Copy files

cp -r /source /destination
  • cp copies files or directories.
  • -r means recursive — required when copying a directory, so it copies the folder and everything inside it. Without -r, cp will refuse to copy a directory.

cp --reflink=auto /source /destination

Instead of duplicating the file's data on disk immediately, --reflink creates a shallow clone that shares the same underlying data blocks as the original. New blocks are only written when one of the two copies is modified (copy-on-write), so the initial copy is nearly instant and uses no extra space until changes are made.

  • --reflink=auto — use a reflink if the filesystem supports it (Btrfs, XFS, APFS); silently fall back to a normal copy if not.
  • --reflink=always — fail with an error if a reflink is not possible, rather than falling back.
Filesystem support required

Reflinks require a filesystem that supports copy-on-write extents. On Ubuntu, Btrfs and XFS (with reflink=1 mount option) support this. The default ext4 filesystem does not.


Move or rename files

mv /source /destination
  • mv moves a file or directory to a new location.
  • If the destination is in the same folder with a different name, it renames the file instead. No flags needed for basic usage.

Bulk rename files with rename

Ubuntu ships with two different rename tools with incompatible syntax:

PackageCommand syntaxSupports regex
util-linux (default)rename <from> <to> <files>No — literal strings only
rename (Perl)rename 's/pattern/replacement/' <files>Yes

Check which version you have:

rename --version

If the output mentions util-linux, you have the basic version.

util-linux rename — literal string replacement:

# rename every .txt file to .bak
rename .txt .bak *.txt

Perl rename — regex-based (install first):

sudo apt install rename
# remove [video_id] suffixes from filenames, e.g. "Title [abc123].mkv" → "Title.mkv"
rename 's/ \[[^\]]*\]//' *.mkv

# replace spaces with underscores
rename 's/ /_/g' *.mkv

# dry run — preview changes without renaming
rename -n 's/ \[[^\]]*\]//' *.mkv
  • -ndry run: print what would be renamed without making any changes.
  • s/pattern/replacement/ — Perl substitution expression; g at the end replaces all occurrences per filename.
Prefer the for loop if Perl rename is unavailable

If you can't install the Perl version, use a for loop with sed as a portable alternative:

for f in *.mkv; do mv "$f" "$(echo "$f" | sed 's/ \[[^]]*\]//')"; done

# symbolic (soft) link — like a shortcut
ln -s /path/to/original /path/to/link

# hard link — a second name for the same file data
ln /path/to/original /path/to/link

A symbolic link points to a path. If the original is moved or deleted, the symlink breaks. Works across filesystems and on directories.

A hard link points directly to the same inode (the underlying data) as the original. The file's data is only deleted when all hard links to it are removed. Cannot span filesystems or link directories.

SymlinkHard link
Survives original deletionNo — becomes a dangling linkYes — data stays until all links removed
Works across filesystemsYesNo
Can link directoriesYes (with -s)No
Shows as separate fileYesYes

Verify a symlink with ls -la — the -> arrow shows what it points to:

ls -la /path/to/link

Delete a file

rm /path/to/file
  • rm permanently deletes a file. There is no recycle bin — deleted files cannot be recovered easily.

Delete a directory

rm -rf /path/to/directory
  • -r means recursive — delete the directory and everything inside it.
  • -f means force — skip confirmation prompts and do not show errors for missing files.
Irreversible

rm -rf is permanent and irreversible. Double-check the path before running it.


Find a file by name

find / -name "filename" 2>/dev/null
  • find searches for files and directories.
  • / is the starting directory — here it starts from the root, searching the entire system.
  • -name "filename" filters results to only entries matching that name. You can use wildcards, e.g. -name "*.log" finds all .log files.
  • 2>/dev/null redirects error messages (like "Permission denied") away from your screen so the output is clean. 2> redirects stderr (error output) and /dev/null is a special file that discards anything written to it.

Find large files and display their sizes

sudo find ./ -type f -size +30G -printf '%s %p\n' | sort -n | numfmt --field=1 --to=iec

Searches the current directory recursively for files larger than 30 GB, then sorts them by size and prints human-readable sizes (e.g. 14T).

  • ./ — start searching from the current directory.
  • -type f — match only files (not directories).
  • -size +30G — only include files larger than 30 GB. Change 30G to any size you need (e.g. 1G, 500M).
  • -printf '%s %p\n' — print the file size in bytes (%s) followed by the full path (%p) on each line. This format is required for the next two commands to work correctly.
  • sort -n — sort the lines numerically by the first column (the byte size), so the largest files appear last.
  • numfmt --field=1 --to=iec — convert the first field (raw bytes) to a human-readable IEC unit (K, M, G, T), so 15393162788864 becomes 14T.
Adjusting the size threshold

Change +30G to a smaller threshold like +1G if you want to cast a wider net.


Find files by extension

find . -type f \( -iname "*.EXT1" -o -iname "*.EXT2" -o -iname "*.EXT3" \)

Searches the current directory recursively for files matching any of the listed extensions. Add or remove -o -iname "*.EXT" blocks to match more or fewer extensions.

  • . — start from the current directory.
  • -type f — match only files.
  • \( ... \) — groups the extension conditions together as a unit.
  • -iname "*.EXT" — case-insensitive name match. *.EXT matches any filename ending in .EXT regardless of upper/lowercase.
  • -o — means or — the file only needs to match one of the extensions.

Example — find files with suspicious or unknown extensions:

find . -type f \( -iname "*.KDRSXS" -o -iname "*.ECJLLX" -o -iname "*.YFUINC" \)

Find and delete files by extension

find . -type f \( -iname "*.EXT1" -o -iname "*.EXT2" -o -iname "*.EXT3" \) -delete

Same as the search above but adds -delete at the end to permanently remove every matched file.

  • -delete — deletes each file that matches all the preceding conditions. It must come after the match conditions or find will behave unexpectedly.
Do a dry run first

Run the command without -delete first to review what will be removed before committing to the deletion.

Example — delete files with those extensions:

find . -type f \( -iname "*.KDRSXS" -o -iname "*.ECJLLX" -o -iname "*.YFUINC" \) -delete

Get detailed file metadata

stat /path/to/file

Displays detailed metadata about a file: size in bytes, block allocation, inode number, permissions, owner, and all three timestamps (access time, modify time, change time).

stat accepts multiple paths in a single call, which is useful for comparing several files at once:

sudo stat /path/to/file1 /path/to/file2 /path/to/file3

Useful for diagnosing files that are unexpectedly growing — run stat periodically and watch whether the Size and Modify time keep updating.


Hex dump the last N bytes of a file

sudo tail -c 1024 /path/to/file | xxd
  • tail -c 1024 reads the last 1024 bytes of the file. Change 1024 to any byte count.
  • xxd converts the binary data to a hex dump for visual inspection.

Useful for checking whether a file has valid end-of-file data or is still being written. For example, a correctly finalised MKV will have recognisable EBML closing bytes; a file still in progress will show an incomplete block.


Check which process has a file open

sudo lsof /path/to/file
  • Passing a file path to lsof shows every process that currently has that file open, along with its PID, user, and access mode.

Useful when a file is growing unexpectedly, cannot be deleted, or you need to confirm whether a tool has finished writing before moving the file.

File still growing?
  1. Run stat /path/to/file to record the current size.
  2. Run sudo lsof /path/to/file to see what has it open and in what mode (r = read, w = write, u = read+write).
  3. Wait a few seconds, then stat again. If the size increased and lsof shows a write handle, a process is still appending to it.

Watch a file's size in real time

watch -n 1 'stat -c "%s bytes %n" "/path/to/file"'
  • watch -n 1 re-runs the quoted command every 1 second and refreshes the display in place.
  • stat -c "%s bytes %n" prints only the size in bytes (%s) and the filename (%n) — much less noise than a full stat output.

Useful for confirming a file has stopped growing after a transcoding or remux job finishes. Press Ctrl+C to exit watch.


Search file contents with grep

grep "pattern" /path/to/file
  • Prints every line in the file that contains pattern. The pattern can be plain text or a regular expression.

Common flags:

FlagEffect
-rRecurse into subdirectories
-iCase-insensitive match
-nPrefix each match with its line number
-lPrint only the filenames that contain a match, not the lines
-vInvert — print lines that do not match
-cPrint a count of matching lines per file
--include='*.log'Limit recursive search to files matching a glob

Recursive search with line numbers (the most common real-world form):

grep -rn "pattern" /path/to/directory

Filter command output — pipe any command into grep to show only relevant lines:

journalctl -u tdarr | grep -i "error\|warn"

Edit text in files with sed

sed 's/search/replacement/' /path/to/file

sed (stream editor) reads a file line by line and applies an expression to each line. The substitution expression format is s/search/replacement/search is the text (or regex) to find, and replacement is what to put in its place.

Concrete example — change every http:// to https:// in a config file:

sed 's/http:\/\//https:\/\//' /etc/myapp/config.conf

Because sed uses / as the delimiter between s, search, and replacement, any / that appears in the search or replacement text must be escaped as \/ so sed doesn't mistake it for a delimiter.

Common expressions:

ExpressionEffect
s/search/replacement/Replace first match per line
s/search/replacement/gReplace all matches per line (g = global)
s/search/replacement/iCase-insensitive replace
/pattern/dDelete lines that contain pattern
/pattern/pPrint only lines that contain pattern (use with -n)

By default sed prints to stdout without modifying the file. Use -i to edit in place:

# replace all occurrences directly in the file
sed -i 's/search/replacement/g' /path/to/file

# save a backup as file.bak before editing
sed -i.bak 's/search/replacement/g' /path/to/file

sed -n '45,55p' /path/to/file

Prints only lines 45 through 55 of a file without modifying it.

  • -nsuppress the default behavior of printing every line. Without this flag, sed prints all lines plus the matched ones.
  • '45,55p' — the address range 45,55 selects lines 45 to 55; p prints each selected line.

Useful for inspecting a specific section of a large config file without opening it in an editor:

sudo sed -n '45,55p' /etc/sudoers

To print a single line, use the same number for both ends of the range, or just '<N>p':

sed -n '72p' /path/to/file

Compare two files line by line

diff file1 file2

Prints the lines that differ between two files. Lines prefixed with < are only in file1; lines prefixed with > are only in file2.

Common flags:

FlagEffect
-uUnified format — shows a few lines of context around each change (the format used by git diff)
-iIgnore case differences
-wIgnore all whitespace differences
-rRecurse into directories and compare all files
-qQuiet — only report whether files differ, not what differs

Example — compare two JSON dumps side by side in unified format:

diff -u /tmp/before.json /tmp/after.json

Exit code 0 means the files are identical; 1 means they differ.


Compare two files byte by byte

cmp file1 file2

cmp does a binary comparison and reports the byte offset and line number of the first difference it finds, then stops. Unlike diff, it works on any file type (binary, compressed, media).

  • With no flags, prints nothing if the files are identical and reports the first differing byte if not.
  • -l — list all differing bytes (offset and values in octal).
  • -s — silent mode: produce no output, only set the exit code. Useful in scripts.
  • -n <bytes> — compare only the first <bytes> bytes. Use this when the files have different sizes and you only want to check the region that exists in both.

Exit code 0 = identical, 1 = differ, 2 = error.

cmp vs diff

Use diff when you want to see what text changed line by line. Use cmp when you only need to know whether two files are byte-for-byte identical (e.g. verifying a copy or download).

Inspecting the differing region — full workflow

Once cmp reports a differing byte offset, use dd + xxd + diff to see exactly what changed around that position:

# 1. Find where the files first differ (limit to the smaller file's size with -n)
cmp -n 2106826942 file-before.mkv file-after.mkv
# output: files differ: byte 2106824424, line 7876101

# 2. Extract ~4 KB around the differing offset from each file
dd if="file-before.mkv" bs=1 skip=2106824000 count=4000 2>/dev/null | xxd > /tmp/before.hex
dd if="file-after.mkv" bs=1 skip=2106824000 count=4000 2>/dev/null | xxd > /tmp/after.hex

# 3. Diff the two hex dumps to see exactly which bytes changed
diff -u /tmp/before.hex /tmp/after.hex
  • bs=1 skip=<offset> — seek to <offset> bytes into the file (bs=1 means each skip unit is 1 byte).
  • count=4000 — read 4000 bytes (adjust to capture the full area of interest).
  • 2>/dev/null — suppress dd's progress line so only the data goes to xxd.

Browse disk usage interactively with ncdu

ncdu /path/to/directory

ncdu (NCurses Disk Usage) scans a directory tree and displays a sorted, interactive breakdown of what is consuming space.

Install it first if not present:

sudo apt install ncdu

Key bindings once inside ncdu:

KeyAction
/ Move between entries
EnterDrill into a subdirectory
dDelete the selected file or directory (with confirmation)
iShow info about the selected item
qQuit

Useful for quickly finding which folder is eating your disk before reaching for du -sh *.


Copy and convert data at block level

dd if=/dev/sda of=/path/to/image.img bs=4M status=progress

dd reads from an input file (if=) and writes to an output file (of=) block by block. Both if and of can be a regular file, a disk device, or a special file like /dev/zero or /dev/null.

  • if= — input file or device to read from.
  • of= — output file or device to write to.
  • bs= — block size; larger values (e.g. 4M) are faster for bulk transfers.
  • status=progress — print transfer speed and progress while running.

Common uses:

CommandWhat it does
dd if=/dev/sda of=disk.img bs=4M status=progressClone an entire disk to an image file
dd if=disk.img of=/dev/sdb bs=4M status=progressRestore an image file to a disk
dd if=/dev/zero of=testfile bs=1M count=1024 status=progressCreate a 1 GB zero-filled file (useful for write speed testing)
dd if=/dev/urandom of=testfile bs=1M count=100Fill a file with random data
of= targets are overwritten silently

Writing to a wrong of= target (e.g. of=/dev/sda instead of of=/dev/sdb) will destroy data with no confirmation prompt. Double-check the device name with lsblk before running.


User Management

Add a new user

sudo adduser <username>

Creates a new user account interactively — it will ask you to set a password and optional details. Replace <username> with the desired username.


Add a user to a group

sudo usermod -aG <group> <username>
  • usermod modifies an existing user account.
  • -a means append — add the user to the group without removing them from other groups.
  • -G specifies the group to add the user to.
Always use -aG together

Using -G alone without -a will remove the user from all other groups.

Example: sudo usermod -aG docker john gives the user john permission to run Docker commands without sudo.


Switch to another user

su - <username>
  • su stands for substitute user — switches to the specified user's account.
  • - loads that user's full login environment (home directory, shell settings, etc.). Without -, you switch users but keep your current environment, which can cause unexpected behaviour.

Show current user

whoami

Prints the username of the currently logged-in user. Useful when switching between users to confirm who you are.


List groups for a user

groups <username>

Prints all groups the specified user belongs to. Leave out <username> to see groups for the current user.


Service Management (systemd)

Ubuntu uses systemd to manage background services (called units or daemons). Replace <service> with the service name, e.g. nginx, ssh, or docker.

Start a service

sudo systemctl start <service>

Starts the service immediately. This does not make it start automatically on reboot.


Stop a service

sudo systemctl stop <service>

Stops the service immediately.


Restart a service

sudo systemctl restart <service>

Stops and then starts the service. Use this to apply configuration changes.


Enable a service at boot

sudo systemctl enable <service>

Configures the service to start automatically every time the system boots. Does not start it right now.


Disable a service at boot

sudo systemctl disable <service>

Prevents the service from starting automatically on boot. Does not stop it right now.


Check service status

sudo systemctl status <service>

Shows whether the service is running or stopped, and displays the most recent log lines. Press q to exit.


View live service logs

journalctl -u <service> -f
  • journalctl reads logs managed by systemd.
  • -u <service> means unit — filter logs to only show entries from the specified service.
  • -f means follow — keep the output open and print new log lines in real time (like tail -f). Press Ctrl+C to stop.

Networking

Show IP addresses

ip a
  • ip is the modern Linux networking tool.
  • a is short for address — lists all network interfaces and their assigned IP addresses (both IPv4 and IPv6).

Show routing table

ip route

Displays the system's routing table — which network interface handles traffic to which destinations, and what the default gateway is.


Test connectivity

ping -c 4 <host>
  • ping sends ICMP echo requests to a host to test if it is reachable.
  • -c 4 means count 4 — send exactly 4 packets then stop. Without -c, ping runs forever until you press Ctrl+C.

Example: ping -c 4 google.com


DNS lookup

nslookup <domain>

Queries DNS to find the IP address associated with a domain name. Example: nslookup google.com.


Show open ports

ss -tulnp
  • ss stands for socket statistics — shows network connections and listening ports.
  • -t shows TCP connections.
  • -u shows UDP connections.
  • -l shows only listening sockets (ports waiting for connections).
  • -n shows numeric addresses and port numbers instead of resolving hostnames and service names.
  • -p shows the process (program name and PID) that owns each socket.

Find what is using a specific port

ss -tulnp | grep :<port>

Filters the socket list to only show the process bound to a specific port. Replace <port> with the port number.

Example — find what is using port 8080:

ss -tulnp | grep :8080

The output shows the process name and PID in the last column, e.g. users:(("nginx",pid=1234,fd=6)).

You can also use lsof for a more detailed view:

sudo lsof -i :<port>
  • lsof stands for list open files — on Linux, network sockets are treated as files.
  • -i :<port> filters to only show processes with a socket open on that port.
  • The output includes the command, PID, user, and connection state.

Or use fuser to get just the PID instantly:

sudo fuser <port>/tcp
  • fuser prints the PID(s) of processes using the specified port. Combine with -v for verbose output including the process name:
sudo fuser -v <port>/tcp
Port conflict workflow
  1. Run sudo lsof -i :<port> to identify the conflicting process and its PID.
  2. Either stop the service (sudo systemctl stop <service>) or kill the process (sudo kill -9 <pid>).
  3. Then start your own service on that port.

Test if a port is reachable

nc -zv <host> <port>
  • nc (netcat) opens a TCP connection to <host> on <port> and immediately exits.
  • -z — zero-I/O mode: connect and exit without sending data (port probe only).
  • -v — verbose: print whether the connection succeeded or was refused.

Example — check if Jellyfin on another machine is reachable:

nc -zv 192.168.1.50 8096

Output when open: Connection to 192.168.1.50 8096 port [tcp/*] succeeded!

Output when blocked or closed: nc: connect to 192.168.1.50 port 8096 (tcp) failed: Connection refused

Port blocked vs. port closed

Connection refused means the host responded but nothing is listening on that port — the service is down or on a different port.

Connection timed out (no output for several seconds) means the traffic is being dropped by a firewall before it reaches the host — check UFW rules on the server and any network-level firewall between the two machines.


Check firewall status (UFW)

sudo ufw status verbose
  • ufw stands for Uncomplicated Firewall — Ubuntu's built-in firewall tool.
  • status shows whether the firewall is active and lists the current rules.
  • verbose shows extra detail, including the default policies for incoming and outgoing traffic.

Allow a port through UFW

sudo ufw allow <port>/tcp

Creates a firewall rule that permits incoming TCP connections on the specified port. Replace <port> with the port number, e.g. sudo ufw allow 80/tcp opens port 80 for web traffic.


Allow a port with a comment

sudo ufw allow <port>/tcp comment '<description>'

Same as above but attaches a human-readable label to the rule so you can remember why it was added. The comment appears when you run ufw status.

Example:

sudo ufw allow 8096/tcp comment 'Jellyfin Web UI'

View firewall rules with rule numbers

sudo ufw status numbered

Lists all active firewall rules with a number next to each one. The numbers are useful for deleting a specific rule:

sudo ufw delete <number>

Example — to remove rule number 3:

sudo ufw delete 3

Process Management

List running processes

ps aux
  • ps shows a snapshot of currently running processes.
  • a shows processes from all users, not just your own.
  • u shows output in a user-oriented format that includes the username, CPU%, and memory% columns.
  • x includes processes that are not attached to a terminal (background daemons).

Interactive process viewer

top

Opens a live, updating view of all running processes sorted by CPU usage. Useful for spotting what is using the most resources. Press q to quit, M to sort by memory, P to sort by CPU.


Interactive process viewer (enhanced)

htop

htop is a more readable alternative to top with color-coded bars for CPU and memory, mouse support, and easier process management. Install it first if not present:

sudo apt install htop

Key bindings:

KeyAction
F2Open settings
F3 / /Search for a process by name
F5Toggle tree view (shows parent/child relationships)
F6Change sort column
F9Send a signal to the selected process (e.g. SIGKILL)
qQuit

Kill a process by PID

kill -9 <pid>
  • kill sends a signal to a process.
  • -9 is the signal number to send — signal 9 is called SIGKILL, which forces the process to terminate immediately and cannot be ignored or caught by the program. You can also write it as -SIGKILL for the same effect. Use this only when a normal kill <pid> (which sends SIGTERM) has not worked.
  • <pid> is the process ID — the target of the signal. You can find it with ps aux or pgrep.

So in the example below, -9 is what to do and 8325 is who to do it to:

sudo kill -9 8325

Kill a process by name

pkill <process-name>

Sends a termination signal to all processes matching the given name. Easier than finding the PID first.


Find a process by name

pgrep <process-name>

Prints the PID(s) of all processes matching the given name. Example: pgrep nginx.


Find processes by full command line

pgrep -a -f <pattern>
  • -a — print the full command line alongside each PID, not just the PID.
  • -f — match against the full command line (including arguments and flags) instead of just the executable name. Useful when multiple processes share the same binary (e.g. several concurrent ffmpeg jobs with different input files).

The pattern can be a regex, so you can match multiple programs at once using | alternation:

sudo pgrep -a -f 'mkvpropedit|tdarr|ffmpeg'

This returns all running processes whose command line contains mkvpropedit, tdarr, or ffmpeg. sudo is needed to see processes owned by other users (e.g. services running as root or a dedicated service account).


Find a process with ps and grep

sudo ps auxww | grep '[m]kvpropedit'
  • ps auxww lists all processes for all users (a), with user-oriented format (u), including processes without a controlling terminal (x), with no column truncation (ww).
  • The bracket trick [m]kvpropedit prevents the grep process itself from appearing in results (the pattern doesn't match its own command line).

Alternative to pgrep when you want the full ps output columns (USER, PID, %CPU, %MEM, VSZ, RSS, START, TIME, COMMAND) without a separate lookup.


Inspect a specific process by PID

sudo ps -p <PID> -o pid,etime,pcpu,pmem,cmd
  • -p <PID> — target a single process by its PID.
  • etime — elapsed time since the process started (format: [[DD-]HH:]MM:SS).
  • pcpu / pmem — CPU and memory percentage.
  • cmd — full command line.

Useful after finding a PID with pgrep or ps auxww to check how long a job has been running and how much CPU/memory it is consuming.


Trace file-write syscalls on a running process

sudo strace -p <PID> -e trace=write,pwrite64,truncate,ftruncate -c
  • -p <PID> — attach to an already-running process.
  • -e trace=write,pwrite64,truncate,ftruncate — record only file-write related syscalls; filters out the noise of unrelated syscalls.
  • -c — summary mode: instead of printing every call, accumulate counts and print a table of call frequency and time when you detach (Ctrl+C).

Useful for confirming whether a process is actively writing to disk. If write/pwrite64 counts climb while you watch, the process is still appending data. A near-zero count after a few seconds means it has stalled or finished.


Permissions

Linux file permissions control who can read, write, or execute a file. Every file has three sets of permissions: one for the owner, one for the group, and one for everyone else (others).

Understanding permission numbers

Each set is represented by a single digit from 0–7, calculated by adding up the values of the permissions granted:

ValuePermissionSymbol
4Readr
2Writew
1Executex
0No permission-

You add these numbers together to get the digit for each set. For example, read + write = 4 + 2 = 6.

Permission reference chart

NumberPermissionsSymbolicWho can do what
7Read + Write + ExecuterwxFull access
6Read + Writerw-Read and modify, but not execute
5Read + Executer-xRead and run, but not modify
4Read onlyr--View only
3Write + Execute-wxModify and run, but not read
2Write only-w-Modify only
1Execute only--xRun only
0No permission---No access

Common permission combinations

ModeOwnerGroupOthersTypical use
755rwxr-xr-xDirectories, executables — owner can edit; everyone else can read/run
644rw-r--r--Regular files — owner can edit; everyone else can only read
600rw-------Private files (e.g. SSH keys) — only the owner can read/write
777rwxrwxrwxFull access for everyone — avoid unless necessary
700rwx------Private executables — only the owner can read, write, or run
750rwxr-x---Group-accessible executables — others have no access

Change file permissions

chmod 755 /path/to/file
  • chmod stands for change mode — sets the permissions of a file or directory.
  • The three digits represent permissions for owner, group, and others respectively.

Example: chmod 644 config.txt makes config.txt readable by everyone but only writable by the owner.

To apply permissions recursively to a directory and all its contents, add -R:

chmod -R 755 /path/to/directory

Change file owner

sudo chown -R <user>:<group> /path
  • chown stands for change owner — changes who owns a file or directory.
  • <user>:<group> sets both the owning user and the owning group. You can omit :<group> to change only the user.
  • -R means recursive — apply the change to the directory and everything inside it.

Example: sudo chown -R john:john /home/john/projects gives the user john ownership of all files in that directory.


SSH

SSH (Secure Shell) lets you securely connect to and control remote machines from your terminal.

Connect to a remote host

ssh <user>@<host>

Opens an encrypted terminal session on the remote machine. Replace <user> with the username on the remote machine and <host> with its IP address or hostname.

Example: ssh john@192.168.1.10


Copy files to a remote host

scp /local/file <user>@<host>:/remote/path
  • scp stands for secure copy — copies files over SSH.
  • The first argument is the source file on your local machine.
  • The second argument is the destination in the format user@host:/path.

To copy a directory, add -r (recursive):

scp -r /local/folder <user>@<host>:/remote/path

Copy files from Windows to Ubuntu (SCP)

scp -r "C:\path\to\folder" <user>@<host>:/remote/directory/

Run this command from a local Windows terminal (PowerShell or Command Prompt), not from inside your remote SSH session.

  • Windows 10 and 11 include the OpenSSH client by default.
  • Wrap paths containing spaces in double quotes.
  • The remote path uses standard Linux forward slashes (/).

Example:

scp -r "C:\Users\John\Documents\MEGA downloads\Season 01" server@192.168.1.10:/media/merged/Downloads/
Run SCP from the machine sending the files

If you are logged into your Ubuntu server via SSH and try to run scp to pull from Windows, Ubuntu tries to connect back to your Windows PC over port 22. Unless Windows has OpenSSH Server installed, running, and allowed through the firewall, the command will hang. Always push the files from a local Windows terminal instead.


Generate an SSH key pair

ssh-keygen -t ed25519 -C "your_email@example.com"

Creates a pair of cryptographic keys for passwordless SSH authentication.

  • -t ed25519 specifies the key type — Ed25519 is modern and secure.
  • -C "your_email@example.com" adds a comment to the key to help you identify it later.

This creates two files: a private key (~/.ssh/id_ed25519) that you keep secret, and a public key (~/.ssh/id_ed25519.pub) that you share with remote servers.


Copy your public key to a remote host

ssh-copy-id <user>@<host>

Appends your public key to the remote machine's ~/.ssh/authorized_keys file, enabling passwordless login. You only need to do this once per remote machine.


Rsync

rsync is a fast, versatile file copying tool that only transfers the parts of files that have changed, making it far more efficient than cp or scp for large transfers or regular backups.

Common flags

FlagMeaning
-aArchive — preserves permissions, timestamps, symlinks, owner, and group. Equivalent to -rlptgoD. Almost always used.
-vVerbose — prints the name of each file as it is transferred.
-zCompress — compresses data during transfer to reduce bandwidth. Useful over slow networks, unnecessary on a LAN.
-hHuman-readable — shows transfer sizes in KB/MB/GB.
-PProgress + Partial — shorthand for --progress --partial. Shows a per-file progress bar and keeps partially transferred files so the transfer can be resumed.
--progressProgress — shows a per-file progress bar without enabling partial file resuming.
--info=progress2Overall progress — shows a single progress bar for the entire transfer instead of one per file. Cleaner output for large jobs.
-nDry run — simulates the transfer without actually copying anything. Use this first to verify what will be synced.
--deleteDeletes files at the destination that no longer exist at the source, keeping the two locations in sync.
--excludeSkips files or directories matching a pattern.

Copy a local directory to another local location

rsync -avh /source/directory/ /destination/directory/
  • -a preserves all file attributes.
  • -v prints each file name as it copies.
  • -h shows sizes in human-readable format.
Trailing slash matters

/source/directory/ (with trailing slash) copies the contents of the directory into the destination. /source/directory (no trailing slash) copies the directory itself into the destination, creating /destination/directory/directory/.


Preview what will be synced (dry run)

rsync -avhn /source/directory/ /destination/directory/
  • -n performs a dry run — nothing is copied. Use this to verify the file list before committing.

Copy files to a remote host over SSH

rsync -avhP /local/directory/ <user>@<host>:/remote/directory/
  • -P shows a per-file progress bar and enables resuming interrupted transfers.
  • rsync uses SSH by default for remote transfers.

Example:

rsync -avhP /home/john/videos/ john@192.168.1.10:/mnt/storage/videos/

Copy files from a remote host to local

rsync -avhP <user>@<host>:/remote/directory/ /local/directory/

Same as above but the source and destination are swapped — pulling files from the remote machine to your local machine.


Copy files from Windows to Ubuntu with Rsync (WSL or Git Bash)

To push files from Windows to an Ubuntu server using rsync with progress and resume support, run the command from WSL (Windows Subsystem for Linux) or Git Bash on your Windows machine:

From WSL (type bash or wsl in PowerShell): In WSL, local Windows drives are mounted under /mnt/ (e.g. C:\ becomes /mnt/c/):

rsync -avhP "/mnt/c/Users/John/Documents/downloads/Season 01" <user>@<host>:/remote/directory/

From Git Bash (right-click in the folder → Open Git Bash here): In Git Bash, local Windows drives are mounted directly under / (e.g. C:\ becomes /c/):

# If launched from outside the folder (using absolute path):
rsync -avhP "/c/Users/John/Documents/downloads/Season 01" <user>@<host>:/remote/directory/

# If launched using "Open Git Bash here" inside the folder (using relative path):
rsync -avhP "./Season 01" <user>@<host>:/remote/directory/
Dry run first

Add -n (rsync -avhnP ...) to simulate the transfer and verify file paths before copying data.


Sync and delete files removed from source

rsync -avh --delete /source/directory/ /destination/directory/
  • --delete removes files from the destination that no longer exist in the source, keeping the destination an exact mirror.
Do a dry run first

--delete permanently removes files from the destination. Always do a dry run with -n first.


Exclude files or directories

rsync -avh --exclude='*.tmp' --exclude='cache/' /source/ /destination/
  • --exclude='*.tmp' skips all files ending in .tmp.
  • --exclude='cache/' skips the cache directory entirely.
  • You can stack multiple --exclude flags.

Resume an interrupted transfer

rsync -avhP --append-verify /source/ /destination/
  • --append-verify resumes partially transferred files and verifies the checksum of the appended data. Safer than --append alone.

Media File Inspection

These commands require mkvtoolnix and ffmpeg. Install them with:

sudo apt install mkvtoolnix ffmpeg

Inspect MKV container structure

mkvinfo /path/to/file.mkv | head -100
  • mkvinfo parses an MKV file and prints its full container structure: EBML headers, tracks, codecs, timestamps, and flags.
  • | head -100 limits output to the first 100 lines — remove it to see everything (output can be thousands of lines for large files).

Useful for identifying the codec of each track (e.g. VC-1, H.264, AC-3) before remuxing or transcoding, and for diagnosing container-level issues such as a corrupt seek head.

To save the full output to a file for easier reading (e.g. in a text editor or with grep):

mkvinfo /path/to/file.mkv > output.txt

Dump MKV metadata as JSON

mkvmerge -J /path/to/file.mkv > /tmp/output.json
  • -J outputs the file's full track, attachment, chapter, and container metadata as structured JSON instead of human-readable text.
  • Redirecting to a file (e.g. /tmp/output.json) lets you open it in a JSON viewer, diff two files, or parse it with jq.

Useful for comparing a file before and after a remux or transcode to confirm track order, codec, language tags, and flags are unchanged:

mkvmerge -J before.mkv > /tmp/before.json
mkvmerge -J after.mkv > /tmp/after.json
diff /tmp/before.json /tmp/after.json

Add track statistics tags to an MKV

mkvpropedit /path/to/file.mkv --add-track-statistics-tags
  • Reads the actual stream data and writes BPS (bitrate), DURATION, NUMBER_OF_FRAMES, and NUMBER_OF_BYTES tags into the MKV container without re-encoding.
  • Tdarr and some media servers call this automatically after transcoding to populate statistics that the original mux may not have included.
  • The file is modified in-place; no re-mux is required.

Ever-growing files with mkvpropedit (affects mkvpropedit < v71.0.0 only)

Files muxed with old versions of mkvmerge (e.g. v5.8.0 from 2012) often have a Seek Head with no void space reserved for future edits. When mkvpropedit needs to add or update Seek Head entries, if the existing Seek Head is too small to hold the new positions, versions before v71.0.0 entered an endless loop writing data to the end of the file — causing the file to grow indefinitely.

This was fixed in MKVToolNix v71.0.0 (issue #3338): the fix was to void the undersized Seek Head and write a new one with sufficient space instead. If you are running mkvpropedit v71.0.0 or newer, this bug does not affect you. Any codec muxed with that era of mkvmerge is susceptible, not just VC-1.

If you are on an older version, use mkvinfo to check the writing application and mkvmerge version in the file's segment info before running mkvpropedit, and remux the file first with a current mkvmerge to rebuild the Seek Head with proper void space.


Probe media file metadata

ffprobe -v error -show_entries format=filename,format_name,duration,size -of default=noprint_wrappers=1 /path/to/file
  • -v error — suppress all output except errors.
  • -show_entries format=... — select which container-level fields to display. Common fields: filename, format_name, duration, size, bit_rate.
  • -of default=noprint_wrappers=1 — output as plain key=value pairs without section headers.

Example output:

filename=Supernatural - S02E15 - Tall Tales.mkv
format_name=matroska,webm
duration=2612.345678
size=34359738368

To also include per-stream info (codec, resolution, audio channels), add stream=codec_name,width,height,channels to -show_entries:

ffprobe -v error -show_entries format=size,duration:stream=codec_name,width,height -of default=noprint_wrappers=1 /path/to/file
Confirm a file is no longer growing

Run ffprobe (or stat) twice a few seconds apart. If the size value changes between runs, a process is still writing to the file.


Buy me a beer

Changelog

docs(ubuntu): generalize placeholder paths in rsync commands
docs(ubuntu): add Windows to Ubuntu file transfer guides for SCP, WSL, and Git Bash
docs(ubuntu): add rename tool section with util-linux and Perl versions
docs(ubuntu): add sed -n line range print command
docs(ubuntu): add cmp -n flag and dd+xxd+diff forensic workflow


💬 Discord Community Chat

Join the conversation! Comments here sync with our Discord community.

💬 Recent Comments

Loading comments...