Shell Prompt and Command Syntax
Every time you open a Linux terminal, you’re looking at a shell prompt – a short line of text waiting for you to type a command. Understanding what that prompt is telling you, and exactly how the shell reads and executes what you type, is the foundation for everything else in this course. Get the syntax right and the shell does precisely what you ask; get it slightly wrong – an extra space, a missing quote, a stray dash – and it can do something completely different, sometimes destructively.
Overview: how the shell reads and runs a command
The shell is a program whose job is to read text you type, work out what you mean, and ask the kernel to run it. On almost every Linux distribution the default interactive and scripting shell is Bash (the "Bourne Again SHell"), and that’s what this course targets throughout. When Bash is ready for input, it prints a prompt and waits.
A typical prompt looks like this:
alice@webserver:~$
Reading left to right, that string tells you: the logged-in username (alice), the hostname of the machine (webserver), and the current working directory (~, which is shorthand Bash uses for your home directory). The character right before the cursor matters: a plain $ means you’re an ordinary user; a # means the shell is running as root, the superuser who can bypass normal permission checks. That single character is worth a glance every time, since a command that’s merely annoying as a regular user (like accidentally deleting your own files) can take down the whole system as root.
The exact text of the prompt is controlled by the environment variable PS1 – you can inspect your own with echo "$PS1", though customizing it is a topic for a later lesson. What matters here is the part after the prompt: the line you type.
When you press Enter, Bash performs, roughly, these steps: it reads the line of text, splits it into tokens (words) based on whitespace while respecting any quotes, expands anything that needs expanding (variables, ~, wildcards, and so on), removes the quote characters themselves, and only then looks at the first word to decide what to run. That first word is looked up, in order, as: a shell alias, a shell keyword (like if or for), a shell function you’ve defined, a builtin command compiled into Bash itself (like cd or echo), and finally, if none of those match, an external program found by searching every directory listed in the PATH environment variable, left to right, for an executable file with that exact name. The first match wins; if nothing matches, you get command not found.
Once Bash finds an external program, it forks – creates a near-identical copy of itself as a new process – and the child process then execs the program, replacing itself with the new program’s code while keeping the same process ID. The parent shell waits for that child to finish. When the program exits, it reports back a small integer called its exit status: 0 means success, any non-zero value (by convention, 1-255) means some kind of failure. Bash stores that number in the special variable $?, which always reflects the exit status of the most recently completed command – so if you want to check it, do so immediately, before running anything else, including a command as simple as echo.
Syntax
Nearly every Linux command follows the same general shape:
command [options] [arguments]
| Part | Meaning |
|---|---|
command |
The program or builtin to run, e.g. ls, grep, cd. |
-a |
A short option (single dash, single letter). Several can often be combined: -la means -l and -a together. |
--all |
A long option (double dash, full word). More readable, especially in scripts, and cannot be combined with other letters. |
-o value |
An option that takes its own argument, e.g. -o output.txt, or with a long option, --output=output.txt. |
-- |
A special marker meaning "stop parsing options – treat everything after this as a plain argument," even if it starts with a dash. |
argument |
The data the command acts on – usually a filename, directory, or piece of text. |
Whitespace (one or more spaces or tabs) is what separates these pieces; Bash doesn’t care how many spaces you use, only that there’s at least one, and that the pieces are in the order the specific command expects. Options usually – but not always – come before arguments, and for options that take a value, the value must directly follow that option.
Examples
Example 1: anatomy of a simple command
ls -l /etc/hosts
-rw-r--r-- 1 root root 220 Jan 15 09:32 /etc/hosts
Here ls is the command, -l is a short option requesting the "long" listing format, and /etc/hosts is the argument telling ls which file to describe. The shell tokenizes the line into exactly three words, finds ls in /usr/bin via PATH, and runs it with -l and /etc/hosts passed as two separate arguments.
Example 2: combining short options
tar -xzvf backup.tar.gz
backup/config.yaml
backup/data.db
backup/notes.txt
-xzvf is four short options bundled into one token: -x (extract), -z (decompress gzip), -v (verbose – print each file as it’s processed), and -f (the next argument is the archive filename). Bash treats -xzvf as a single word and passes it to tar unchanged; it’s tar itself, not the shell, that splits the bundled letters apart. This only works for short single-letter options, and only for programs written to support it – it’s a convention, not a shell feature.
Example 3: using -- to stop option parsing
rm -- -oldfile.txt
removed '-oldfile.txt'
Suppose a file is literally named -oldfile.txt – perhaps created by accident. Running rm -oldfile.txt would make rm interpret -oldfile.txt as a string of unknown options and fail. Placing -- before the filename tells rm (and most well-behaved commands) that everything after it is a plain argument, never an option, so the dash-prefixed filename is handled correctly.
How it works step by step
Walking through ls -l /etc/hosts end to end:
- Bash reads the full line of input up to the Enter key.
- It splits the line into tokens on whitespace:
ls,-l,/etc/hosts. - It performs any expansions present (none here – no variables, no
~, no wildcards). - It checks
lsagainst aliases, keywords, functions, and builtins – none match, so it searchesPATHdirectory by directory until it finds an executable namedls, typically/usr/bin/ls. - Bash forks a child process, and that child execs
/usr/bin/ls, handing it-land/etc/hostsas its argument list. - The parent shell waits while
lsreads the file’s metadata from the filesystem and prints a formatted line to standard output. - When
lsfinishes, it exits with status0(success); Bash stores that in$?and prints a fresh prompt.
Common Mistakes
1. Forgetting to quote a variable that contains spaces.
file="monthly report.txt"
rm $file
Without quotes, Bash word-splits the expanded variable into two separate arguments, monthly and report.txt, and rm tries to delete two files that likely don’t exist – or worse, ones that do. Always quote variable expansions:
file="monthly report.txt"
rm "$file"
2. Missing the space between the command and its options.
ls-l /etc/hosts
bash: ls-l: command not found
Bash tokenizes on whitespace, so ls-l is treated as one word – a single, nonexistent command name – rather than ls with a -l option. The fix is simply to include the space:
ls -l /etc/hosts
3. Treating a filename that starts with a dash as if it were plain text.
rm -oldfile.txt
rm: invalid option -- 'o'
Because the argument begins with -, rm assumes it’s an option string and rejects the unrecognized letters. As shown in Example 3, use -- to force everything after it to be treated as a plain argument:
rm -- -oldfile.txt
Best Practices
- Prefer long options (
--verbose) over short ones (-v) in scripts you’ll reread later – they’re self-documenting. - Always quote variable and command-substitution expansions:
"$var","$(cmd)","$1". - Check a command’s supported flags with
man <command>or<command> --helpbefore guessing – never invent a flag and hope it exists. - Use
--before filenames that might start with a dash, especially in scripts that operate on user-supplied filenames. - Read
$?immediately after a command whose success matters, before running anything else that would overwrite it. - Glance at the prompt’s trailing character –
$vs#– before running anything destructive, so you always know whether you’re acting as root.
Practice Exercises
- Run
echo "$PS1"in your terminal and identify which parts of the raw string correspond to the username, hostname, and directory you see displayed in your actual prompt. - Take the command
grep -inr "TODO" ~/projectsand write out, in your own words, which part is the command, which letters are which combined options, and which part is the argument. - Create an empty file whose name starts with a dash (hint:
touch -- -example.txt), then figure out the correctlsandrmcommands needed to list and then delete it without triggering an "invalid option" error.
Summary
- The shell prompt shows username, hostname, and current directory, and its trailing
$or#tells you whether you’re a regular user or root. - A command line generally follows
command [options] [arguments], with short (-a) and long (--all) options and a--marker to end option parsing. - Bash tokenizes your input on whitespace, expands variables and wildcards, then looks up the command as an alias, keyword, function, builtin, or an executable found via
PATH. - Running an external program forks a child process, execs the program into it, and waits for it to finish; its exit status lands in
$?, where0means success. - Always quote variable expansions and use
--for dash-prefixed filenames to avoid the shell or the command misinterpreting your intent.
