The Terminal & Shell Basics
The Terminal & Shell Basics
Every server you will ever manage in production runs Linux and accepts commands through a terminal. There are no GUIs, no right-click menus, no drag-and-drop. The terminal is the interface — and mastery of it is the single most leveraged skill a DevOps engineer can develop. Before you navigate filesystems or write pipelines, you need to understand what you are actually talking to when you open that black window.
Terminal, Shell, Console — What Is the Difference?
These three words are used interchangeably in casual conversation but they mean distinct things:
- Terminal (emulator): A program that draws a window, handles keyboard input, and renders text output. Examples:
gnome-terminal,iTerm2,Windows Terminal,alacritty. On a remote server you connect viassh, and the SSH session itself acts as a terminal. - Shell: The program that runs inside the terminal. It reads your commands, interprets them, and hands them to the kernel. The shell is the language interpreter — it has variables, loops, functions, and conditionals. Common shells:
bash(Bourne Again Shell, the Linux default),zsh(macOS default since Catalina),sh(POSIX-compliant minimal shell),fish(friendly interactive shell),dash(ultra-lean, often/bin/shon Ubuntu). - Console: Originally the physical text-mode screen and keyboard attached directly to a server. In modern usage it usually means the terminal you get when you log in locally, or a cloud provider's out-of-band serial console you use when SSH is down.
#!/bin/bash or #!/bin/sh, you are choosing the interpreter — exactly like choosing Python 3 vs Python 2 with #!/usr/bin/env python3. The shebang line matters in production because dash (often /bin/sh on Ubuntu) does not support bash-specific syntax like [[ or local -n. Always use #!/usr/bin/env bash in scripts that need Bash features.Reading the Shell Prompt
When you open a terminal, the shell displays a prompt — a short string that tells you where you are and who you are before waiting for input. Decoding it immediately saves confusion:
hostname and whoami before running anything irreversible. Some teams color-code the shell prompt red for production hosts — a visual interrupt that forces a pause.Anatomy of a Command
Every shell command follows a consistent structure. Understanding this structure lets you read any command you encounter, even one you have never seen before:
Breaking down the example: ls -lh --sort=size /var/log 2>&1 | head -20
ls— the command (list directory contents)-lh— combined short flags:-l(long format) and-h(human-readable sizes)--sort=size— long option with a value: sort by file size/var/log— the argument: directory to list2>&1— redirect stderr (file descriptor 2) into stdout (file descriptor 1) so errors are visible in the pipe| head -20— pipe output tohead, showing only the first 20 lines
Getting Help: man, --help, and Beyond
At big-tech companies, engineers routinely work with commands they have not memorized. The ability to rapidly find authoritative answers is more valuable than memorizing flags. Linux provides several layers of documentation:
command -v instead of which to check whether a program exists. which is not POSIX-standard and behaves inconsistently across distributions — on some systems it returns 0 even when the command is not found. The correct idiom is: if ! command -v docker >/dev/null 2>&1; then echo "docker not found"; exit 1; fiShell Types: Interactive vs Non-Interactive, Login vs Non-Login
This distinction catches every DevOps engineer at least once in production. A shell's behavior — which configuration files it reads — depends on how it was started:
- Login shell: Started when you log in (SSH,
su -,sudo -i). Reads/etc/profile, then~/.bash_profile(or~/.profile). YourPATH,JAVA_HOME, and other environment variables are loaded here. - Non-login interactive shell: A new terminal tab, or
bashwithout-l. Reads~/.bashrc. Aliases and prompt customizations live here. - Non-interactive shell: A shell script running in a CI pipeline, a cron job, or
ssh host "command". Reads almost nothing — no.bashrc, no aliases. This is why a script that works locally fails in CI:/usr/local/binis in your interactivePATHbut not the script's.
Shell History and Keyboard Shortcuts
Production speed comes from not retyping. These shortcuts are muscle memory for experienced engineers:
Ctrl+R— reverse search through history; type part of a command to find itCtrl+A/Ctrl+E— jump to beginning / end of lineCtrl+W— delete one word backwardCtrl+L— clear screen (same asclear)!!— repeat the last command (classic use:sudo !!after forgetting sudo)!$— last argument of the previous command:mkdir /opt/myapp && cd !$Alt+.— insert last argument of previous command (same as!$but interactive)Ctrl+C— send SIGINT to the foreground process (interrupt)Ctrl+Z— suspend the foreground process (thenfgto resume,bgto background)Ctrl+D— send EOF; closes the current shell if at an empty prompt
~/.bash_history (default 500–2000 lines). In production environments, HISTSIZE is often set to a much larger number (50,000+) and HISTTIMEFORMAT is configured so that every command is timestamped — critical for incident investigations that ask "what exactly did the engineer run and when?"Understanding Shells at a Glance
What the Shell Does with Your Input
Understanding shell expansion order prevents subtle bugs in scripts and prevents accidental command injection in CI pipelines:
- Tokenization: Split the input on whitespace (unless quoted)
- Brace expansion:
{a,b,c}→a b c - Tilde expansion:
~→/home/edrees - Parameter/variable expansion:
$VAR→ its value - Command substitution:
$(command)→ its output - Arithmetic expansion:
$((1 + 2))→3 - Word splitting: Split the result of expansion on
IFS(default: space, tab, newline) - Pathname expansion (globbing):
*.log→ matching filenames - Quote removal: Remove the now-processed quotes
"$VAR" not $VAR. Without quotes, a variable containing spaces splits into multiple arguments, breaking commands silently. This is the source of countless production script failures: rm -rf $DEPLOY_DIR with DEPLOY_DIR="" expands to rm -rf which removes the current directory tree.