Scripting Best Practices
The difference between a snippet that works on your laptop and a script you are willing to run from cron on a production host: strict mode, real error handling, linting, deliberate portability choices, and predictable CLI behavior. Senior shell interviews spend most of their time in this area.
part ofBashoverview, primer and where to startread it →on this pageshowhide
explore
- Strict Mode and Error Handling5 questions
- ShellCheck, shfmt and Style5 questions
- POSIX Portability vs Bashisms4 questions
- Argument Parsing with getopts5 questions
- Debugging and Tracing Scripts5 questions
- Temp Files, Locking and Idempotency5 questions
- Handling Untrusted Input Safely4 questions
- Running Scripts from cron and systemd5 questions
questions
page 2 of 2Pull requests that touch the team's bash scripts keep collecting comments about indentation and where `then` should go. What does the shfmt formatter do that ShellCheck does not, and how would you run it so CI fails on unformatted files without rewriting them?
basics
~20 sshfmt is a formatter: it rewrites shell scripts into a canonical layout, while ShellCheck is a linter that finds bugs and never changes the file. In CI use shfmt -d, which prints a diff and exits non-zero instead of writing.
In bash, what changes when a getopts optstring begins with a colon, as in `while getopts ":f:v" opt`, compared with `"f:v"` — and how does the script then tell an unknown option from a missing option value?
basics
~20 sA leading colon puts getopts in silent error mode: bash stops printing its own diagnostics, an unknown option sets the variable to ? and a missing value sets it to :, with the offending option letter in OPTARG so the script can report both itself.
This line works when pasted into an interactive shell but not from a crontab: `0 3 * * * /opt/bin/dump.sh --stamp $(date +%Y%m%d)`. What does cron do with the `%` character in a crontab command, and how do you write the entry correctly?
basics
~20 sIn a crontab, an unescaped percent sign ends the command and becomes a newline: everything after the first one is fed to the command as standard input. Escape each one as %, or move the logic into the script.
A bash script dies with `deploy.sh: line 87: ...` inside a helper function that is called from a dozen places, so the line number alone does not tell you which call path got there. Which bash builtin and which shell arrays let a script print its own call stack?
basics
~20 sBash keeps a call stack you can read: the caller builtin walks it one frame at a time, and the parallel arrays FUNCNAME, BASH_SOURCE and BASH_LINENO expose it directly, so a script can print which function called which, from which file and line.
In a bash script, what does `trap 'handler' ERR` give you that `set -e` alone does not, and why is it usually paired with `set -E`?
basics
~20 sAn ERR trap runs your handler on the same failures that make set -e abort, so the script can report the failing line, command and status instead of dying silently. set -E (errtrace) is needed because otherwise the ERR trap is not inherited by shell functions, command substitutions or subshells.
A deploy script runs `ssh "$host" "rm -rf /srv/app/$release"` where $release comes from a CI variable. The local quoting looks right, so why is this still a command-injection hole, and how do you pass an untrusted value to a remote command safely?
basics
~20 sssh does not send an argument vector; it joins its command arguments into one string that the remote login shell parses again. Local quotes only govern the local parse, so metacharacters in the value become remote syntax. Validate the value, or pass it on stdin.
A team standard says every shell script in the repo must be POSIX-compliant `#!/bin/sh` "for portability". How would you decide whether that policy is right, and what does writing POSIX sh actually cost you?
basics
~20 sDecide from the list of environments the script must actually run in. POSIX sh buys you machines where bash is absent, but costs arrays and [[ ]], which often makes the code less safe rather than more. Guaranteeing bash is usually cheaper.
You own a bash deployment script that colleagues rerun by hand after it fails halfway. What does making that script idempotent actually require, and how do you decide how far to take it?
basics
~20 sIdempotence is about the end state, not the exit status: a second run must leave the system in the same shape as the first, whether or not the first finished. That means classifying every step and converting the ones that accumulate rather than converge.
showing 31–38 of 38