Learning bite
Bash arguments and quoting
Preserve argument boundaries and make a script interface explicit.
On this page
A shell runs commands and connects their results
Bash reads a command, expands its arguments, finds the executable or built-in command, and runs it. A script stores those steps in a file. We will inspect paths, not change services. Run this module in Bash inside the Ubuntu machine; macOS's usual interactive Zsh is a different interpreter.
Create a new directory with mkdir -p ~/foundations-shell, then cd ~/foundations-shell. Use it only for this module's practice files. A variable assignment has no spaces around =:
study_name='file with spaces.txt'
printf 'sample\n' > "$study_name"
printf '<%s>\n' "$study_name"
printf '<%s>\n' $study_name
Expected output from the quoted form is one line, <file with spaces.txt>. The unquoted form produces three lines. Bash split the value into words before printf saw it; unquoted expansions may also expand wildcard patterns into filenames. Single quotes preserve literal characters. Double quotes allow variable expansion while preserving one argument. The value is data, so do not put it into a command string and run eval.
Give a script a clear interface
Save the following as inspect-path.sh in this directory. The quoted EOF prevents your current shell expanding variables while writing the script:
cat > inspect-path.sh <<'EOF'
#!/usr/bin/env bash
if [[ $# -ne 1 ]]; then
printf 'Usage: %s PATH\n' "$0" >&2
exit 2
fi
study_target=$1
if [[ ! -e "$study_target" ]]; then
printf 'Path does not exist: %s\n' "$study_target" >&2
exit 1
fi
printf 'Inspecting: %s\n' "$study_target"
ls -ld -- "$study_target"
EOF
bash inspect-path.sh "$study_name"
bash inspect-path.sh "$HOME"
$# is the argument count, $0 the script name, and $1 its first argument. An if runs the commands after then when its test returns status 0. [[ ... ]] is Bash's conditional syntax; -ne means numerically unequal, -e tests whether a path resolves to an existing object, and ! reverses the test. fi closes the conditional. We check the count before reading $1.
>&2 directs a message to the error stream. exit 2 ends this script with its chosen usage-error status; exit 1 reports a missing target. Otherwise the final ls status becomes the script status. -- ends options for ls, so a name beginning with - is treated as a path. A dangling symbolic link fails -e, and an existing file may disappear before ls; this check is not a lock.
You can also make the script executable with chmod u+x inspect-path.sh and run ./inspect-path.sh .. ./ supplies a path instead of asking Bash to find the program in PATH. The shebang selects Bash for direct execution; sh inspect-path.sh explicitly chooses another shell and may reject [[ ... ]].
Repeat a check without losing argument boundaries
A loop assigns each listed value to a variable and runs its body once per value:
for study_path in . 'file with spaces.txt' './missing-study-path'; do
if bash inspect-path.sh "$study_path"; then
printf 'Inspection succeeded\n'
else
printf 'Inspection failed\n' >&2
fi
done
Expect two successes and one missing-path failure. The spaces in the second path remain part of one argument. A function gives a group of commands a name:
inspect_many() {
local study_path
for study_path in "$@"; do
bash inspect-path.sh "$study_path"
done
}
inspect_many . 'file with spaces.txt'
local keeps the loop variable within the function. "$@" preserves each supplied argument separately; "$*" would combine them into one string. This simple function returns the last command's result, so it is not yet a complete all-inputs success check.
Try no arguments and two arguments, predicting which if runs. Both return usage status 2 without inspecting a path. Keep the script and spaced filename for the testing bite. Next, connect commands using streams and pipes.
Sources
Primary references: Bash quoting↗; Bash conditional expressions↗.
Your notes and evidence
Record observations, questions, or links to your work. Keep credentials out of your notes.
Back up or restore this path
Progress and notes stay in this browser. A backup contains only this learning path.