Marcus Kazmierczak

Home mkaz.blog

Using the Command-line

I live on the terminal. The two windows I always have open: a terminal and a browser. This is a guide to how I use the command-line for development, productivity, and daily computing tasks. Whether you're new to the command-line or looking to level up your skills, this guide provides practical tips and real-world examples that build from foundations to advanced techniques.

Prerequisites

To get the most from this guide, you should:

  • Know how to open a terminal application
  • Understand basic file system concepts (files, folders)
  • Be comfortable typing commands

Terminology used in this guide:

  • Terminal: The application that displays a command-line interface (e.g., Terminal.app, iTerm2, GNOME Terminal)
  • Shell: The program that interprets commands (e.g., bash, zsh, fish)
  • Command-line: The text interface where you type commands

New to terminals entirely? This guide starts with fundamentals and builds progressively. You can stop at your comfort level and return as you grow.


Part 1: Getting Started 🌱

This section covers essential concepts and setup. These are the foundations that everything else builds upon.

Understanding Your Shell Environment

When you open a terminal, you're running a shell - a program that interprets your commands. The most common shells are bash (Bourne Again SHell) and zsh (Z Shell). macOS uses zsh by default; Linux typically uses bash.

Shell configuration files control your environment. When you start a shell:

  • Login shells read ~/.zprofile or ~/.bash_profile
  • Interactive shells read ~/.zshrc or ~/.bashrc

This is where you put aliases, functions, and environment customization. Changes take effect when you open a new terminal or run source ~/.zshrc (or source ~/.bashrc).

Environment variables are settings your shell uses. Key ones:

  • $PATH: Directories where the shell looks for commands
  • $HOME: Your home directory (same as ~)
  • $PWD: Your current working directory

Check a variable: echo $PATH

Essential Setup

Use a Package Manager

On Linux systems, use your distribution's package manager (apt, dnf, pacman). On macOS, use Homebrew, which provides the same streamlined package management experience.

Install Homebrew on macOS:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Then follow the installer's instructions to add Homebrew to your PATH.

Manage Your Dotfiles with Version Control

Your dotfiles (.zshrc, .vimrc, etc.) are your command-line configuration. Use source control to track them and sync across systems. I keep mine in a dotfiles github repo.

Simple dotfiles workflow:

  1. Create a ~/dotfiles directory and initialize git
  2. Move your config files there (e.g., mv ~/.zshrc ~/dotfiles/zshrc)
  3. Create symlinks: ln -s ~/dotfiles/zshrc ~/.zshrc
  4. Commit and push to GitHub

The symlink means edits to ~/.zshrc automatically update your repo. When setting up a new system, clone your repo and recreate the symlinks.

Understanding File Paths

Absolute paths start from the root directory: /Users/mkaz/projects/myapp

Relative paths start from your current location: ./projects/myapp or just projects/myapp

Special path shortcuts:

  • ~: Your home directory
  • .: Current directory
  • ..: Parent directory
  • -: Previous directory (with cd)

Basic navigation:

pwd                 # print working directory (where am I?)
ls                  # list files
ls -la              # list all files (including hidden) with details
cd /path/to/dir     # change directory
cd                  # go straight to home directory
cd -                # return to previous directory

Quick directory movement:

Create these aliases to save time moving up directories:

alias cd..='cd ..'
alias cd...='cd ../../'
alias cd....='cd ../../../'

Advanced: Jump to directories with Zoxide

Zoxide tracks your most-used directories and lets you jump to them with fragments. Install it:

brew install zoxide

Add to your shell config:

eval "$(zoxide init zsh)"  # or bash

Try it now:

  1. Navigate to a project: cd ~/Documents/MyProject
  2. Go somewhere else: cd ~
  3. Jump back instantly: z MyProject

Zoxide ranks directories by frequency and recency. Type z followed by any fragment of a directory name.

Basic File Operations

mkdir dirname           # create directory
mkdir -p path/to/dir    # create nested directories (makes parent dirs)
cp file1 file2          # copy file
cp -r dir1 dir2         # copy directory recursively
mv oldname newname      # move/rename file
rm file                 # remove file
rm -rf dirname          # remove directory and contents (BE CAREFUL!)

Pro tip: Add alias mkdir='mkdir -p' so mkdir always creates parent directories as needed.

Understanding Input and Output Streams

Unix commands work with three streams:

  • stdin (standard input): Data coming into a command
  • stdout (standard output): Normal output from a command
  • stderr (standard error): Error messages from a command

You can redirect these streams:

ls -l > files.txt           # redirect stdout to file (overwrite)
ls -l >> files.txt          # append stdout to file
ls -l 2> errors.txt         # redirect stderr to file
ls -l > /dev/null           # discard stdout (errors still show)
ls -l > /dev/null 2>&1      # discard both stdout and stderr

The pipe operator | connects commands by sending one command's stdout to another's stdin:

ls -l | grep myfile         # list files, filter to lines containing "myfile"

This is the magic glue of Unix - combining simple tools to solve complex problems.


Part 2: Daily Workflows 🔧

These are the productivity techniques that will save you the most time every day.

Time-Saving with Aliases

Aliases let you create shortcuts for commands you run frequently. The average developer types the same commands hundreds of times daily - aliases save thousands of keystrokes.

Create useful aliases:

alias ll='ls -la'
alias ..='cd ..'
alias gs='git status'
alias gd='git diff'

Include sudo in aliases:

alias alog='sudo tail -f /var/log/apache2/error.log'
alias apt='sudo aptitude'

Combine commands:

alias xps='ps -ax'
alias xpsg='ps -ax | grep -i'

Bypass an alias temporarily by prefixing with \: \ls runs the original ls command.

Where to put aliases: Add them to your shell config file (~/.zshrc or ~/.bashrc).

Discover what to alias: Check your most-used commands:

history | awk '{print $2}' | sort | uniq -c | sort -rn | head -10

Create aliases for the commands you run most often.

Powerful Functions

When aliases aren't enough, use shell functions. Functions let you use arguments in flexible ways and add logic.

Simple function example:

# List the last 10 files in any directory
function lsr() {
    ls -lrt "$@" | tail -n 10
}

Usage: lsr /etc/ lists the 10 most recently modified files in /etc/.

Function with error handling:

# Create directory and cd into it
function mkcd() {
    if [ -z "$1" ]; then
        echo "Usage: mkcd <directory>"
        return 1
    fi
    mkdir -p "$1" && cd "$1"
}

Function with conditional logic:

# Quick backup of a file
function backup() {
    if [ ! -f "$1" ]; then
        echo "File $1 does not exist"
        return 1
    fi
    cp "$1" "$1.backup.$(date +%Y%m%d_%H%M%S)"
    echo "Backed up to $1.backup.$(date +%Y%m%d_%H%M%S)"
}

Where to put functions: Add them to your shell config file, or create a separate ~/dotfiles/functions file and source it:

# In your .zshrc or .bashrc
source ~/dotfiles/functions

System-specific configuration:

I bounce between Macs and Linux, so I maintain system-specific configs:

SYS_OS=$(uname -s)
SHORT_HOSTNAME=$(hostname -s)

# Load system-specific aliases
if [[ "$SYS_OS" == "Linux" ]]; then
    source ~/dotfiles/aliases.linux
else
    source ~/dotfiles/aliases.mac
fi

# Load host-specific config if it exists
if [[ -e ~/dotfiles/profile.$SHORT_HOSTNAME ]]; then
    source ~/dotfiles/profile.$SHORT_HOSTNAME
fi

Working with Files

View files without opening an editor:

cat file.txt        # output entire file
head file.txt       # first 10 lines
tail file.txt       # last 10 lines
head -n 5 file.txt  # first 5 lines
tail -n 20 file.txt # last 20 lines
less file.txt       # paginated view (searchable, use q to quit)

I use less for peeking at files - it handles large files well, is searchable (type / then your search term), and there's no risk of accidentally editing.

Count things:

wc -l file.txt      # count lines
wc -w file.txt      # count words
wc -c file.txt      # count bytes

Recursive file operations with globs:

Use ** to recurse through all subdirectories:

ls -l **/*.jpg                  # find all jpg files recursively
grep "TODO" **/*.py             # search all Python files

Text Search with Ripgrep

ripgrep (rg) is my go-to search tool. It's blazing fast, respects .gitignore, and smarter than traditional grep.

Install ripgrep:

brew install ripgrep    # macOS
apt install ripgrep     # Debian/Ubuntu

Most useful patterns:

rg pattern                      # search recursively
rg -i pattern                   # case-insensitive
rg -w word                      # match whole words only
rg -l pattern                   # list files with matches
rg -c pattern                   # count matches per file
rg -v pattern file.txt          # show lines NOT matching
rg -A 2 -B 2 pattern            # show 2 lines before/after match

Search specific file types:

rg pattern --type py            # only Python files
rg pattern --type js            # only JavaScript files
rg --type-list                  # see all available types

Exclude files:

rg jquery --glob '!*.min.js'    # exclude minified JS
rg pattern --glob '!*.log'      # exclude log files

Real-world examples:

rg "function my_func" --type php      # find function definition
rg -l TODO                            # list all files containing TODO
rg "import.*pandas" --type py         # find pandas imports

Command History and Shortcuts

Navigate history:

  • Up/Down arrows: Previous/next command
  • Ctrl-r: Search command history (start typing to search)
  • history: Show recent commands
  • !!: Run previous command
  • sudo !!: Run previous command with sudo

Tag commands for future searching:

Add a comment to commands you'll want to find later:

dd if=debian.img of=/dev/disk2 bs=1m  # bootable-usb

Now use Ctrl-r and search for bootable-usb to find this command instantly.

Vim command-line editing:

Add this to your .vimrc to save files with sudo when you forgot:

" sudo write
ca w!! w !sudo tee >/dev/null "%"

Combining Commands with Pipes

The pipe | chains commands together, taking output from one as input to the next:

Basic piping:

ls -l | grep myfile             # list files, filter to myfile
ps aux | grep python            # find Python processes
cat file.txt | sort | uniq      # output, sort, remove duplicates

Using xargs:

xargs converts piped input into command-line arguments:

ls -1 *.txt | xargs wc -l       # count lines in all .txt files
find . -name "*.log" | xargs rm # find and delete log files

Practical combinations:

# Find and delete all .DS_Store files
find . -name ".DS_Store" | xargs rm -rf

# List installed packages, find ones marked for removal, purge them
dpkg --get-selections | grep deinstall | xargs aptitude purge

# Get list of URLs from file and check HTTP status
cat urls.txt | xargs -I {} curl -I {}

Brace Expansion and Patterns

Brace expansion creates multiple strings from patterns:

echo {one,two,three}-alligator
# Output: one-alligator two-alligator three-alligator

# Create multiple directories at once
mkdir -p app/storage/{cache,logs,meta,sessions,views}

# Backup multiple files
cp config.{yml,yml.backup}
# Expands to: cp config.yml config.yml.backup

# Rename with numbered suffix
mv report.txt report-{1,2,3}.txt

Negative patterns - match everything except:

rm !(*.txt)         # delete all files except .txt files
ls !(*.log|*.tmp)   # list all except .log and .tmp files

Note: Negative patterns require shopt -s extglob in bash.

Text Editing on the Command-line

Why learn a terminal editor?

When you're working on remote servers or in a pure terminal environment, GUI editors aren't available. Terminal editors open instantly and integrate seamlessly with command-line workflows.

Vim is my choice - powerful, ubiquitous, and lightning fast once you learn it. I've written an extensive Working with Vim guide covering everything from basics to advanced techniques.

Vim survival guide:

  • vim filename: Open file
  • i: Enter insert mode (start typing)
  • Esc: Exit insert mode
  • :w: Save
  • :q: Quit
  • :wq: Save and quit
  • :q!: Quit without saving

How I use Vim: As a text editor, not an IDE. The command-line is my IDE. Vim opens so quickly that I constantly open and close it. If I'm editing a file it's open; if not it's closed.


Part 3: Next Level âš¡

Modern Tools

There are many modern alternatives to classic Unix tools that work better and easier to use.

Comparison:

Task Traditional Modern Alternative Key Improvement
List files ls eza Better colors, Git integration, tree view
Find files find fd Simpler syntax, respects .gitignore
Search text grep ripgrep Much faster, smarter defaults
System monitor top htop Interactive, better UI, easier navigation
Fuzzy finder - fzf Interactive filtering for anything
Change directory cd zoxide Frecency-based directory jumping
Prompt Complex $PS1 Starship Beautiful, easy config, fast

Use aliases to transition smoothly:

alias ls='eza'
alias find='fd'
alias grep='rg'
alias top='htop'

Your muscle memory stays the same, but you get modern tool benefits.

Installing modern tools:

# macOS
brew install eza fd ripgrep htop fzf zoxide starship

# Debian/Ubuntu
apt install fd-find ripgrep

# Many tools also have pre-built binaries on GitHub

Setup Modern Tools

Starship prompt:

Starship makes your prompt beautiful and informative without complex $PS1 configuration.

Install and enable:

brew install starship
echo 'eval "$(starship init zsh)"' >> ~/.zshrc

Customize by editing ~/.config/starship.toml. See Starship docs for options.

fzf fuzzy finder:

fzf adds interactive filtering to anything. Install:

brew install fzf
$(brew --prefix)/opt/fzf/install  # installs key bindings

Key bindings:

  • Ctrl-r: Search command history (better than default)
  • Ctrl-t: Search files in current directory
  • Alt-c: Change to subdirectory

Use fzf in scripts:

# Let user select a file interactively
selected_file=$(find . -type f | fzf)
vim "$selected_file"

Automation: Loops and Scripts

One-line loops for repetitive tasks:

# Backup all .txt files
for file in *.txt; do cp "$file" "$file.bak"; done

# Convert all .png to .jpg
for img in *.png; do convert "$img" "${img%.png}.jpg"; done

# Run command on list of servers
for server in web1 web2 web3; do ssh "$server" 'uptime'; done

Basic bash script structure:

Create script.sh:

#!/bin/bash
set -e  # exit on error

echo "Starting task..."

for ITEM in file1 file2 file3; do
    echo "Processing $ITEM"
    # your commands here
done

echo "Done!"

Run with: bash script.sh

Script best practices:

  • Start with #!/bin/bash (shebang)
  • Use set -e to exit on errors
  • Quote variables: "$var" not $var
  • Check exit codes: if [ $? -eq 0 ]; then

Learn more: Bash Guide for Beginners

Project Automation with Justfiles

For project-specific tasks, I recommend just - a modern command runner with better defaults than Make.

Why just over Make:

  • Easier syntax (no tab nightmares)
  • Better error messages
  • Works cross-platform
  • More intuitive for scripting tasks

Install:

brew install just

Create justfile in project root:

# Build the project
build:
    npm run build

# Run tests
test:
    npm test

# Run linter
lint:
    eslint src/

# Development workflow: build, test, lint
dev: build test lint

# Clean build artifacts
clean:
    rm -rf dist/ node_modules/

Run commands:

just build
just test
just dev      # runs multiple commands

List available commands: just --list

This creates a consistent interface across all your projects. New team members can run just --list to see available commands.

Scheduling with Cron

Cron runs commands on a schedule - perfect for backups, updates, and routine maintenance.

Edit crontab:

crontab -e

Cron syntax:

* * * * * command
│ │ │ │ │
│ │ │ │ └─ day of week (0-7, 0 and 7 are Sunday)
│ │ │ └─── month (1-12)
│ │ └───── day of month (1-31)
│ └─────── hour (0-23)
└───────── minute (0-59)

Examples:

# Run backup daily at 2am
0 2 * * * /home/user/backup.sh

# Run script every 15 minutes
*/15 * * * * /home/user/check-status.sh

# Run on first of month
0 0 1 * * /home/user/monthly-report.sh

See my tutorial: How to use Unix crontab

Timing and Delays

Time a command:

time sleep 5
time python slow-script.py

Shows real time, user CPU time, and system CPU time.

Delay execution:

sleep 5; echo "5 seconds later"
sleep 60 && notify-send "Timer done"  # minute timer with notification

Working with Remote Systems

Simplify SSH with config

Instead of: ssh -l marcus.kazmierczak remote.server.example.com

Create ~/.ssh/config:

Host remote
    HostName remote.server.example.com
    User marcus.kazmierczak
    Port 22
    IdentityFile ~/.ssh/id_rsa

Now just: ssh remote

Use SSH keys instead of passwords:

  1. Generate key pair: ssh-keygen -t ed25519
  2. Copy public key to server: ssh-copy-id user@server
  3. Now login without password: ssh user@server

SSH agent for convenience:

Instead of typing your key passphrase repeatedly, use ssh-agent:

eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519

Add to your shell config to run automatically.

More details: How to setup SSH keys

Copy files with rsync:

rsync efficiently copies files, only transferring changes. Great for deployments and backups.

# Copy directory to remote server
rsync -avzC local-dir/ user@server:/remote-dir/

# Flags explained:
# -a: archive mode (preserves permissions, timestamps)
# -v: verbose
# -z: compress during transfer
# -C: auto-ignore like CVS (skips .git, .svn, etc.)

Dry run first:

rsync -avzC --dry-run local/ remote:/path/

Shows what would be transferred without actually copying.

More examples: Rsync command examples

HTTP Debugging with curl

curl is essential for testing APIs, debugging sites, and scripting HTTP interactions.

Most useful flags:

curl -I https://example.com          # dump response headers only
curl -v https://example.com          # verbose (see full request/response)
curl -X POST https://api.example.com # specify HTTP method
curl -d "param=value" url            # send POST data
curl -H "Auth: token" url            # add custom header

Download files:

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

Follow redirects:

curl -L https://example.com

Test API:

curl -X GET https://api.example.com/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

Learn more: Art of HTTP Scripting

Extract Archives

One command for tar archives (no need to remember flags):

tar xvfz tarball.tar.gz     # extract .tar.gz
tar xvfj tarball.tar.bz2    # extract .tar.bz2

Mnemonic: "eXtract Verbosely the File, you Zealot/Bzip2-er"


Part 4: Specialized Uses 📚

These are optional topics you can explore based on your specific needs.

Password Management

Generate secure passwords:

brew install pwgen
pwgen 32        # generate 32-character password
pwgen -s 20     # generate 20-char password (more secure)

Advanced: pass password manager

pass is a Unix-philosophy password manager using GPG encryption. Setup requires GPG knowledge but provides secure, command-line accessible password storage.

File Manipulation Utilities

Batch rename files:

Note: rename utility varies between systems. On macOS, install via Homebrew. Linux has Perl version shown here.

# Change file extensions
rename 's/JPG/jpg/' *.JPG

# Replace spaces with dashes
rename 'y/ /-/' *

# Add prefix to files
rename 's/^/backup-/' *.txt

Random lines from file:

shuf -n 1 quotes.txt        # one random line
shuf -n 5 data.txt          # five random lines

Document Conversion

Pandoc converts between text formats - Markdown, HTML, PDF, Word, and many more.

Install:

brew install pandoc

Common conversions:

pandoc input.md -o output.html           # Markdown to HTML
pandoc input.md -o output.pdf            # Markdown to PDF
pandoc input.md -o output.docx           # Markdown to Word
pandoc slides.md -t beamer -o slides.pdf # Markdown to Beamer slides

See Pandoc documentation for supported formats and options.

Presentations with Markdown

Use MARP to create slide decks from Markdown. Write slides in plain text, generate HTML/PDF presentations.

Install:

npm install -g @marp-team/marp-cli

Create slides in slides.md:

# My Presentation

---

## Slide 2

- Bullet points
- More content

---

## Slide 3

Code blocks, images, and tables all work

Generate presentation:

marp slides.md --html       # generate HTML
marp slides.md --pdf        # generate PDF

Full guide: Markdown Slides with MARP

Email from Command-line

Sending email from command-line requires mail agent configuration (like msmtp or ssmtp). Setup can be tricky - consider if you really need this capability.

Once configured:

# Email command output
command | mail -s "subject" user@example.com

# Email file contents
mail -s "subject" user@example.com < file.txt

# Attach file
echo "See attached" | mail -s "subject" -a file.pdf user@example.com

Task Management Tools

Todo.txt

Simple system using plain text files to track tasks. Available at todotxt.com for multiple platforms.

Taskwarrior

Feature-rich command-line todo manager at taskwarrior.org with many plugins and third-party apps.

My own tool

I created a tasks management tool that displays a TUI kanban board.


Troubleshooting

Command Not Found

Problem: command not found error

Solutions:

  1. Check if command is installed: which command-name
  2. Install the package: brew install package-name
  3. Check if in PATH: echo $PATH
  4. Find where installed: find /usr -name command-name 2>/dev/null

Permission Denied

Problem: Permission denied error

Solutions:

  1. Check file permissions: ls -l filename
  2. Run with sudo: sudo command (be careful!)
  3. Change ownership: sudo chown $USER filename
  4. Make executable: chmod +x script.sh

Reading Documentation

Man pages (manual pages) document most commands:

man ls          # read ls documentation
man grep        # read grep documentation

Navigate man pages:

  • Space: next page
  • b: previous page
  • /search: search for text
  • q: quit

Quick help:

command --help      # most commands have help flag
command -h          # sometimes -h works too

Checking Exit Codes

Commands return exit codes (0 = success, non-zero = error):

command
echo $?             # print exit code of last command

Use in scripts:

if command; then
    echo "Success!"
else
    echo "Command failed"
fi

Quick Reference

Essential Commands Cheat Sheet

Navigation: - pwd - print working directory - ls -la - list all files with details - cd path - change directory - cd - - previous directory

Files: - cat file - output file - less file - view file (paginated) - head file - first 10 lines - tail file - last 10 lines - wc -l file - count lines

Search: - rg pattern - search recursively - rg -i pattern - case-insensitive search - find . -name "*.txt" - find files by name

System: - ps aux - show all processes - top or htop - monitor system - df -h - disk space - du -sh * - directory sizes

Keyboard Shortcuts: - Ctrl-c - cancel current command - Ctrl-r - search command history - Ctrl-l - clear screen - Ctrl-a - beginning of line - Ctrl-e - end of line - Tab - autocomplete

File Permissions Primer

Permissions shown by ls -l:

-rwxr-xr-x
│││││││││└─ execute (others)
││││││││└── write (others)
│││││││└─── read (others)
││││││└──── execute (group)
│││││└───── write (group)
││││└────── read (group)
│││└─────── execute (owner)
││└──────── write (owner)
│└───────── read (owner)
└────────── file type (- = file, d = directory)

Change permissions:

chmod +x script.sh      # make executable
chmod 755 script.sh     # rwxr-xr-x
chmod 644 file.txt      # rw-r--r--

Additional Resources

The command-line gives you direct, unmediated access to your computer. It's faster, more scriptable, and more powerful than any GUI. Invest in learning it well - it's one of the highest-leverage skills you can develop as someone who works with computers.