Skip to content

Command Line Fundamentals🔗

Part of Essentials

This is the first article in the Essentials series — the foundation every Linux professional must own cold. It assumes basic CLI familiarity (you've opened a terminal, run commands before, know what a path is).

If you've worked with Windows CMD or PowerShell, you already understand the concept: type a command, get output. What's different on Linux isn't the concept — it's that the shell is far more capable, and the productivity gap between someone who knows the shell's built-in tools and someone who doesn't is enormous.

This article closes that gap. The engineers who navigate Linux efficiently aren't faster typists — they've internalized about a dozen keyboard shortcuts and patterns that the shell gives you for free.


Anatomy of a Command🔗

Before you can get fast, you need to understand what you're actually typing. Most Linux commands follow this structure:

graph LR
    A["⚙️ COMMAND\nls"] --> D["📤 Output"]
    B["🔧 OPTIONS\n-l -h"] --> D
    C["📁 ARGUMENT\n/var/log"] --> D

    style A fill:#2d3748,stroke:#d69e2e,stroke-width:2px,color:#fff
    style B fill:#2d3748,stroke:#68d391,stroke-width:2px,color:#fff
    style C fill:#2d3748,stroke:#63b3ed,stroke-width:2px,color:#fff
    style D fill:#1a202c,stroke:#cbd5e0,stroke-width:2px,color:#fff
Command Anatomy in Practice
ls -lh /var/log
# ^  ^^ ^^^^^^^
# |  |  argument: the directory to list
# |  options: -l (long format) + -h (human-readable sizes)
# command: list directory contents

Short Options vs Long Options🔗

Most commands offer two forms for every option: a compact single-character form (prefixed with -) and a descriptive word form (prefixed with --). They do the same thing.

Short and Long Options Are Equivalent
ls -l                               # (1)!
ls --format=long                    # (2)!

grep -r 'error' /var/log            # (3)!
grep --recursive 'error' /var/log   # (4)!
  1. Short form.
  2. Long form — same result.
  3. Short form.
  4. Long form — same result.

Short options can be combined. Long options cannot.

Combining Short Options
ls -l -h -t         # (1)!
ls -lht             # (2)!
  1. Works, but verbose.
  2. Same thing — combined short options.

When to use which:

  • Short options in commands you type interactively — faster
  • Long options in scripts — self-documenting, much easier to read months later

Arguments: What the Command Acts On🔗

Arguments are the targets: files, directories, patterns, or other input. Some commands require them; most accept them.

Commands With and Without Arguments
ls              # (1)!
ls /etc         # (2)!
ls /etc /var    # (3)!
  1. Lists the current directory — no argument means default.
  2. Lists /etc — an explicit argument.
  3. Multiple arguments — lists both.

Order Matters

Options typically come before arguments. Some commands are strict about this. When in doubt, follow the pattern command [options] [arguments].


Where You've Seen This🔗

If you've used PowerShell, you already know tab completion. If you've used Docker CLI, kubectl, git, or the Python REPL, you've used tab completion there too — because it's built into the terminal and Bash, not the individual tools. Everything in this article applies across all of them.

The history shortcuts (Ctrl+R, !!, !$) are Linux/macOS-specific patterns, but the concept — not retyping commands you've already run — is universal. PowerShell has PSReadLine for the same purpose. What Linux engineers have is a richer, keyboard-driven version that's been stable for decades.

Command chaining (&&, ||, ;) maps directly to what you'd write in a batch file or PowerShell script with -and, -or, and sequencing. The Linux version is more concise and more composable.


The Shell's Productivity Superpowers🔗

This is where the real gap between beginners and professionals lives. These features are built into Bash and work on any Linux system — no installation required.

  • Tab Completion


    Why it matters: Tab completion is the single biggest speed multiplier at the command line. It prevents typos, completes paths you can't remember, and shows you available options.

    Tab Completion in Action
    ls /var/log/ng<TAB>
    # expands to: ls /var/log/nginx/
    
    systemctl restart post<TAB>
    # expands to: systemctl restart postgresql
    

    Key insight: If nothing happens when you press Tab, it means there's more than one match. Press Tab twice to see all completions.

    Double Tab: See All Options
    ls /etc/sys<TAB><TAB>
    # sysconfiguration/   sysctl.conf    sysctl.d/
    # → now you can type more to narrow it down
    
  • History Search (Ctrl+R)


    Why it matters: Every engineer runs long, complex commands. Ctrl+R lets you find any command you've run before in seconds — without scrolling through history line by line.

    Reverse History Search
    # Press Ctrl+R, then start typing
    (reverse-i-search)`docker': docker compose up -d --build
    # Press Ctrl+R again to cycle through older matches
    # Press Enter to run, or Right arrow to edit first
    

    Key insight: Type just a few unique characters from the command you're looking for. k8s, prod, deploy — whatever's distinctive.

  • History Expansion


    Why it matters: Often you want to re-run the last command slightly modified, or pass the last argument to a new command. History expansion does this in 2-3 keystrokes.

    History Expansion Shortcuts
    cat /etc/nginx/nginx.conf
    sudo !!                          # (1)!
    # → sudo cat /etc/nginx/nginx.conf
    
    mkdir /opt/myapp/config
    cd !$                            # (2)!
    # → cd /opt/myapp/config
    
    diff /etc/hosts /etc/hosts.bak
    cp !^                            # (3)!
    # → cp /etc/hosts
    
    1. !! repeats the entire last command — here, re-running it with sudo prepended.
    2. !$ is the last argument of the previous command.
    3. !^ is the first argument of the previous command.

    Key insight: sudo !! is one of the most useful patterns in Linux — you run a command, realize you needed sudo, and just prepend it.

  • Cursor Shortcuts


    Why it matters: You've typed a long command and need to fix something in the middle. These shortcuts let you navigate and edit without reaching for the mouse.

    Cursor Shortcuts
    Ctrl+A    # (1)!
    Ctrl+E    # (2)!
    Alt+B     # (3)!
    Alt+F     # (4)!
    
    Ctrl+W    # (5)!
    Ctrl+K    # (6)!
    Ctrl+U    # (7)!
    Ctrl+L    # (8)!
    
    1. Jump to the beginning of the line.
    2. Jump to the end of the line.
    3. Jump back one word.
    4. Jump forward one word.
    5. Delete the word before the cursor.
    6. Delete from the cursor to the end of the line.
    7. Delete the entire line.
    8. Clear the screen (same as clear).

    Key insight: Ctrl+W is especially useful — it deletes the last word, letting you fix a path or argument without retyping the whole command.


History search covers most cases, but knowing the other ways to work with history rounds out your toolkit.

History Navigation
                       # (1)!
                       # (2)!

history                 # (3)!
# 1001  ls -lh /var/log
# 1002  cat /etc/nginx/nginx.conf
# 1003  sudo systemctl restart nginx
# 1004  history

!1003                   # (4)!
# → sudo systemctl restart nginx

history | grep docker   # (5)!
# 987  docker ps
# 991  docker logs myapp
# 995  docker compose up -d
  1. Previous command — the arrow keys scroll through history one command at a time.
  2. Next command.
  3. View your full command history.
  4. Re-run a specific command from history by its number.
  5. Search history for matching commands.
Persist History Across Sessions

By default, Bash only keeps a limited history and loses it between terminal sessions. Add these to your ~/.bashrc to fix that:

Better History Settings (~/.bashrc)
export HISTSIZE=10000                     # (1)!
export HISTFILESIZE=20000                 # (2)!
export HISTCONTROL=ignoredups:erasedups   # (3)!
shopt -s histappend                       # (4)!
  1. Keep 10,000 commands in memory.
  2. Keep 20,000 commands on disk.
  3. No duplicate entries.
  4. Append instead of overwriting.

After editing, run source ~/.bashrc to apply.


Command Chaining🔗

Running commands one at a time is fine. Chaining them together is what you'll see in every real-world script and shell session.

Exit Codes: The Glue of Chaining🔗

Every command exits with a numeric status code. 0 means success. Anything else means failure.

Checking Exit Codes
ls /etc/nginx
echo $?
# 0   → success, directory exists

ls /etc/doesnotexist
echo $?
# 2   → failure, directory not found

You don't usually check $? directly in interactive sessions — but it's what the chaining operators use behind the scenes.

The Three Chaining Operators🔗

Runs the second command regardless of whether the first succeeded.

Semicolon: Sequential Execution
apt update ; apt upgrade   # (1)!
  1. Runs apt upgrade even if apt update fails.

Use when the second command doesn't depend on the first.

Runs the second command only if the first succeeded (exit code 0).

AND: Conditional Execution
mkdir /opt/myapp && cd /opt/myapp   # (1)!
git pull && ./deploy.sh             # (2)!
  1. Only cd if mkdir succeeded — protects you from cd-ing into a directory that doesn't exist.
  2. Only deploy if the pull succeeded.

This is the pattern you'll see most in production scripts. It's defensive — if something fails early, the rest doesn't run.

Runs the second command only if the first failed.

OR: Fallback Execution
command_that_might_fail || echo "Something went wrong"   # (1)!

grep -q 'nginx' /etc/hosts || echo "nginx not found in hosts"
  1. Prints the message only if the command failed.

Use for fallbacks, error messages, or default behavior.

Combining Chains🔗

These operators compose naturally:

Combining Chains in Practice
mkdir /opt/myproject && cd /opt/myproject && git init                              # (1)!
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak && vim /etc/nginx/nginx.conf    # (2)!
  1. Create a directory, move into it, and initialize a git repo — && stops the chain if any step fails.
  2. Back up a config file, then edit it — the edit only runs if the backup succeeded.

Chain or Script?

If you find yourself writing the same chain more than twice, turn it into a shell script. But start with chains — they're fast to prototype and easy to reason about.


Common Scenarios🔗

Use tab completion to explore.

You know the file is somewhere in /var/log but you can't remember the exact path.

Exploring With Tab
ls /var/log/<TAB><TAB>
# Shows everything in /var/log/

ls /var/log/nginx/<TAB><TAB>
# access.log   error.log

tail -f /var/log/nginx/access.log

Pattern: Tab twice to see options, type a few characters to narrow down, Tab to complete.

Use Ctrl+R to find it.

You ran a complex find command last week and want to run it again.

Finding Previous Commands
# Press Ctrl+R
(reverse-i-search)`find': find /var/log -name "*.log" -mtime -7 -size +10M
# Press Enter to run immediately
# Press Right arrow to edit before running

Pattern: Press Ctrl+R, type something distinctive from the command (like the directory or a flag), hit Enter.

Use !! to avoid retyping.

Adding Sudo Without Retyping
cat /etc/sudoers
# cat: /etc/sudoers: Permission denied

sudo !!
# → sudo cat /etc/sudoers

Pattern: sudo !! is muscle memory for experienced engineers.

Use cursor shortcuts to fix it without retyping.

You typed a long command but the path at the beginning is wrong.

Fixing a Typo Mid-Command
tail -f /var/lgo/nginx/access.log   # (1)!
#              ^^^
  1. You typed /var/lgo/ by mistake. Ctrl+A jumps to the start of the line, Alt+F skips forward word by word, and Ctrl+W deletes /var/lgo/ so you can retype /var/log/.

Pattern: Ctrl+A to jump to start, Alt+F to hop over words, Ctrl+W to delete and retype the bad part.


Quick Reference🔗

Keyboard Shortcuts🔗

Shortcut What It Does
Tab Complete command, path, or filename
Tab Tab Show all possible completions
/ Scroll through history
Ctrl+R Reverse search through history
Ctrl+A Jump to beginning of line
Ctrl+E Jump to end of line
Alt+B Jump back one word
Alt+F Jump forward one word
Ctrl+W Delete word before cursor
Ctrl+K Delete from cursor to end of line
Ctrl+U Delete entire line
Ctrl+L Clear the screen
Ctrl+C Cancel current command
Ctrl+D Exit the shell (or send EOF)

History Expansion🔗

Shortcut What It Does
!! Entire last command
!$ Last argument of previous command
!^ First argument of previous command
!1003 Run command #1003 from history
history \| grep X Search history for X

Chaining Operators🔗

Operator Behavior
; Run second command regardless
&& Run second command only if first succeeded
\|\| Run second command only if first failed

Practice Exercises🔗

Exercise 1: Tab Completion Practice

Without using your arrow keys or retyping, navigate to /var/log/ and list the contents using only Tab completion.

Then try: complete systemctl stat<TAB> — what does it expand to?

Solution
Tab Completion Navigation
cd /var/log/        # (1)!
ls                  # (2)!
systemctl stat<TAB>
# → systemctl status
  1. Type cd /var/l then Tab to complete.
  2. List what's here.

If Tab didn't complete systemctl stat, press Tab twice to see if there are multiple completions.

Exercise 2: History Recall

Run this command:

Run First
ls -lh /var/log/

Now, without retyping it, use Ctrl+R to find and run it again. Then use !$ to cd into the same directory.

Solution
History Recall
# Press Ctrl+R, type 'var/log' — you'll see the ls command
# Press Enter to run it

cd !$       # (1)!
# → cd /var/log/
  1. !$ = the last argument of the previous command = /var/log/.
Exercise 3: Chaining Practice

Write a single chained command that:

  1. Creates a directory /tmp/testproject
  2. Only if that succeeded, creates a file inside it called README.md
  3. Only if that succeeded, prints "Setup complete"
Solution
Chained Setup Command
mkdir /tmp/testproject && touch /tmp/testproject/README.md && echo "Setup complete"

The && operator ensures each step only runs if the previous one succeeded. If mkdir fails (directory already exists), neither touch nor echo will run.

Exercise 4: The sudo !! Pattern

Run the following command and get a permission denied error:

This Will Fail
cat /etc/sudoers

Then, without retyping the command, run it with sudo.

Solution
sudo !! Pattern
cat /etc/sudoers
# cat: /etc/sudoers: Permission denied

sudo !!
# → sudo cat /etc/sudoers

!! is replaced by the shell with the entire previous command before execution.


Quick Recap🔗

The command line fundamentals every Linux professional owns cold:

  • Command anatomy: COMMAND [OPTIONS] [ARGUMENTS] — short options combine (-lht), long options self-document (--human-readable)
  • Tab completion: your #1 speed tool — one Tab to complete, two Tabs to see all options
  • Ctrl+R: find any previous command by typing a few characters
  • History expansion: sudo !! (repeat last command), cd !$ (last argument), !1003 (by number)
  • Cursor shortcuts: Ctrl+A/E to navigate, Ctrl+W to delete a word, Ctrl+L to clear
  • Chaining: ; (always), && (on success), || (on failure) — the backbone of shell automation

Further Reading🔗

Command References🔗

  • man bash — the complete Bash reference manual (long, but comprehensive)
  • man history — the history command in detail
  • bash --version — which version of Bash you're running

Deep Dives🔗

Official Documentation🔗


What's Next?🔗

You've got the fundamentals. Now you need to actually navigate the Linux filesystem — and that means understanding where everything lives.

Head to Filesystem Hierarchy to learn the structure every Linux system shares, why /etc is for configuration, why /var is for variable data, and where to look for logs, binaries, and application files.

Once you understand the layout, Finding Files will show you how to locate anything on a system quickly — whether it's a config file, a log, or a binary you're not sure where was installed.