Core shell concepts
shell
A program that reads commands you type and runs them — the interface between you and the operating system.
# bash, zsh, fish -- all shells, each with slightly different syntax
💡 Your "terminal" is the window; the "shell" is the program running inside it interpreting your commands.
standard streams
The three default I/O channels every command has: stdin (input), stdout (normal output), stderr (error output).
command > out.txt 2> err.txt # stdout to one file, stderr to another
💡 stdout and stderr are separate streams even though both usually print to your screen — that's why redirecting one doesn't silence the other.
exit code
A number a command returns when it finishes — 0 means success, anything else signals a specific kind of failure.
ls /nonexistent
echo $? # prints the exit code of the last command
💡 Scripts commonly check "$?" or use "&&"/"||" to react to success/failure without printing the code explicitly.
environment variable
A named value available to a shell session and the processes it launches — used for config, paths, and secrets.
export API_KEY=abc123
echo $API_KEY
💡 "export" makes a variable visible to child processes; without it, the variable only exists in the current shell.
Pipes & redirection
pipe
Sends the stdout of one command directly into the stdin of the next, chaining commands together.
cat access.log | grep "500" | wc -l
💡 The core idea behind "do one thing well" Unix philosophy — small tools combined instead of one giant do-everything program.
redirection
Sending a command's output to a file (or reading input from one) instead of the default terminal.
echo "hello" > file.txt # overwrite
echo "world" >> file.txt # append
sort < unsorted.txt
💡 ">" always overwrites the whole file; ">>" always appends — mixing them up is a classic way to accidentally lose data.
grep
Searches text for lines matching a pattern (plain text or regex) and prints the matches.
grep -i "error" app.log
grep -r "TODO" src/
💡 "-i" ignores case, "-r" searches recursively, "-v" inverts the match (prints non-matching lines).
sed
Stream editor — transforms text on the fly, most commonly for find-and-replace, without opening a file interactively.
sed 's/foo/bar/g' file.txt # replace all "foo" with "bar"
💡 GNU sed and BSD/macOS sed have subtly different flags for in-place editing (-i vs -i "") — a classic cross-platform gotcha.
awk
A pattern-scanning and text-processing language, especially good at working with columnar (whitespace/CSV-delimited) data.
awk '{print $1, $3}' data.txt # print columns 1 and 3
💡 "$0" means the whole line, "$1" the first field, "NF" the number of fields — a small but powerful mini-language.
Permissions
chmod
Changes a file's permissions — who can read, write, or execute it.
chmod 755 script.sh
chmod +x script.sh
💡 The three digits mean owner/group/others; 7=read+write+execute, 5=read+execute, 4=read only.
chown
Changes the owner (and optionally the group) of a file or directory.
sudo chown www-data:www-data /var/www/app
💡 Usually needs root privileges — a common step when deploying files as one user that a service needs to run as another.
sudo
"Superuser do" — temporarily runs a single command with elevated (root) privileges.
sudo systemctl restart nginx
💡 Distinct from switching to the root user entirely ("su") — sudo elevates just one command, then returns to your normal permissions.
permission bits (rwx)
Read, write, and execute flags shown in a file listing, applied separately to the owner, the group, and everyone else.
ls -l script.sh
# -rwxr-xr-- 1 alice devs 220 Jul 8 script.sh
💡 That output means: owner can read/write/execute, group can read/execute, others can only read.
Processes
process
A running instance of a program, with its own memory space, identified by a process ID (PID).
ps aux | grep node
💡 A "process" is a running program; a "thread" is a lighter-weight unit of execution within a process.
daemon
A background process that runs continuously with no interactive terminal — typically a long-running service.
# nginx, sshd, dockerd -- all daemons
💡 Pronounced "DEE-mon", not "day-mon" — from the same word as the Greek mythological sense of a background spirit.
signal
A notification sent to a process to tell it to do something — stop, reload config, terminate immediately.
kill -9 1234 # SIGKILL — immediate, unstoppable termination
kill -15 1234 # SIGTERM — polite request to shut down
💡 SIGTERM lets a process clean up (close connections, flush data); SIGKILL gives it no chance to react at all.
background / foreground job
A command running in the background (&) continues without blocking your terminal; a foreground job holds it until finished.
long_task.sh & # runs in the background
jobs # list background jobs
fg %1 # bring job 1 back to the foreground
💡 Closing the terminal usually kills background jobs too, unless started with "nohup" or run inside something like tmux/screen.
nohup
Runs a command so it keeps running even after the terminal that started it closes.
nohup npm run build > build.log 2>&1 &
💡 Short for "no hang up" — from the days terminals were literally connected over phone lines that could "hang up".
Filesystem
absolute vs relative path
An absolute path starts from the filesystem root (/) and works from anywhere; a relative path is interpreted from the current directory.
/home/alice/project/file.txt # absolute
../project/file.txt # relative
💡 Scripts run from cron or CI often break because a relative path assumed a working directory that wasn't actually current.
symlink
A symbolic link — a special file that points to another file or directory, similar to a shortcut.
ln -s /opt/app/current /opt/app/live
💡 Widely used in zero-downtime deploys: swap the symlink to a new release directory atomically, instead of moving files around.
glob pattern
A wildcard pattern the shell expands into matching filenames before running the command.
rm *.log # expands to every .log file in the current dir
cp src/*.ts dist/
💡 The shell does the expansion, not the command itself — that's why "echo *.txt" prints filenames, not the literal string "*.txt".
dotfile
A file or directory whose name starts with a dot — hidden from a default "ls" listing, commonly used for config.
ls -a # shows dotfiles too
.bashrc, .gitignore, .env
💡 The leading dot is purely a convention, not a real permission or security boundary — anyone who knows the name can still access it.
Scripting
shebang
The special first line of a script (#!/bin/bash) that tells the OS which interpreter should run it.
#!/usr/bin/env bash
echo "hello"
💡 "#!/usr/bin/env bash" is more portable than a hardcoded "#!/bin/bash" path, since it finds bash wherever it is on PATH.
PATH
An environment variable listing the directories the shell searches, in order, when you type a command name.
echo $PATH
# /usr/local/bin:/usr/bin:/bin
💡 "command not found" almost always means the executable isn't in any directory listed in PATH — not that it doesn't exist.
alias
A shortcut that expands to a longer command when typed — purely for convenience in interactive shells.
alias ll='ls -la'
alias gs='git status'
💡 Aliases only exist in interactive shells by default — a script calling the same shortcut won't find it unless explicitly sourced.
subshell
A new, child shell process spawned to run a command or block, whose variables don't leak back into the parent shell.
(cd /tmp && ls) # parentheses run in a subshell
pwd # you're still in the original directory
💡 Explains why "cd" inside a subshell — or inside a script run as a separate process — doesn't change your current terminal's directory.