›What's the difference between [ ] and [[ ]] in Bash?
[ ] is the POSIX test command — portable across shells, but it word-splits unquoted variables and globs them,
so [ $var = foo ] can break in surprising ways if $var contains spaces or is empty. [[ ]] is a Bash keyword
that doesn't word-split or glob unquoted variables, supports &&/|| and pattern matching directly, and gives
clearer syntax errors — prefer it in any script that doesn't need POSIX sh portability.
›Why should you quote variable expansions like "$var"?
An unquoted expansion undergoes word splitting (on whitespace) and pathname globbing before the command sees it —
a value containing a space becomes multiple arguments, and a value containing * gets expanded against files in
the current directory. Quoting disables both, so the command receives exactly the value the variable holds.
›What does set -euo pipefail do, and why put it at the top of scripts?
-e exits immediately on any command that fails (rather than continuing with a non-zero exit code silently
ignored). -u errors on referencing an unset variable, instead of treating it as empty. -o pipefail makes a
pipeline's exit code reflect the first failing stage instead of only the last. Together they turn a script from
"keeps running after something breaks" into "stops the moment something's wrong" — the difference between a
scripting bug and a corrupted production run.
›What's the difference between $* and $@?
Both expand to all positional arguments. Unquoted, they behave the same. Quoted, they differ: "$*" expands to a
single string with arguments joined by the first character of IFS; "$@" expands to each argument as its own
separate, still-quoted word — almost always what you actually want when looping over arguments.
›What does local do inside a Bash function, and why does it matter?
Declares a variable scoped to the function, rather than the global shell environment. Without it, every variable a
function sets is global by default — a helper function using a generically-named variable like count or result
can silently overwrite a variable of the same name the caller was using.
›How do you capture a command's output into a variable?
Command substitution: result=$(some_command). The backtick form result=`some_command` does the same thing
but doesn't nest cleanly and is considered legacy — prefer $(...).
›What's the difference between $? and $! ?
$? holds the exit status of the most recently executed foreground command. $! holds the process ID of the most
recently backgrounded command (started with &) — used to later wait on it or kill it by PID.
›What does 2>&1 mean, and why does order matter?
Redirects file descriptor 2 (stderr) to wherever file descriptor 1 (stdout) currently points. Order matters because
redirections are applied left to right: command > file.log 2>&1 sends both streams to the file, because stdout
is redirected first; command 2>&1 > file.log sends stderr to the terminal and only stdout to the file, because
stderr was pointed at the terminal (stdout's target at that moment) before stdout was redirected.
›How would you debug a Bash script that isn't doing what you expect?
bash -x script.sh prints every command with its expansions applied, right before executing it — the fastest way
to see what actually ran versus what the script "looks like" it does. For a specific section, wrap it in
set -x / set +x instead of tracing the whole script.