Bash & the POSIX Shell Module 14
Portability: POSIX sh, versions and platforms interview questions
Interview questions on “Portability: POSIX sh, versions and platforms”: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
What makes a feature a bashism, and how would you find one before it reaches a server?
Answered in the lesson POSIX sh vs Bash: spotting and fixing bashisms
-
Fresher to mid
Name four shell-language changes that POSIX.1-2024 added, and one utility change.
Answered in the lesson What POSIX.1-2024 changed for shell scripts
-
Fresher to mid
A Linux script uses sed -i to edit a file in place and fails on macOS. What is the smallest change that runs on both?
Answered in the lesson GNU vs BSD tools: Linux and macOS differences
-
Fresher to mid
A repository README says the script needs Bash 4 or newer. What one-line check at the top of the script makes that promise hold, and where exactly does it belong?
Answered in the lesson Bash 3.2 to 5.3: versions and feature checks
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Fresher to staff
A container entrypoint starts with
#!/bin/shand worked on a Mac. It must stop unless run as root, checked with[ "$EUID" -ne 0 ], and loads settings withconf=$(< app.conf). On an Ubuntu-based image, run as a non-root user, it logs one error line, starts with empty settings and exits 0. Explain both failures and fix them.- What it tests
- Tracing two near-silent failures under dash to Bash-only features, explaining why the check let the user through, and rewriting it so that any error refuses to run, in every POSIX shell.
- First step
- Run the entrypoint under dash yourself, as a non-root user, and read every line it prints. Then run
shellcheck -s shon it, which names Bash-only constructs that dash runs without a message. For each one, work out what dash does with it and what theifor the assignment receives. - Follow-ups
-
- Why did the root check let a non-root user through instead of stopping?
- Would
set -eat the top have stopped either failure? - How do you write the root check so that any error refuses to run?
- What test in the pipeline would catch this before the image ships?
From the lesson POSIX sh vs Bash: spotting and fixing bashisms
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Fresher to staff
A teammate's deploy script starts with
#!/usr/bin/env bash -eu. It runs on their Mac with both options on, but on the Ubuntu runner it stops before its first command with "No such file or directory" and status 127. Explain the difference, pick a first line for both, and say what options there can never guarantee.- What it tests
- Explaining a failure that happens before any line of the script runs, from how each system passes the rest of a first line to
env, and choosing where shell options belong so they hold however the script starts. - First step
- Notice that no command of the script ran: the error comes from starting it. Write down which arguments
envreceives from that first line on each system, then start the script both as./deploy.shand asbash deploy.shand compare which options are on. - Follow-ups
-
- Why does the same first line work on the Mac?
- Is
#!/usr/bin/env -S bash -eua good fix? - What happens to the options when someone runs
bash deploy.sh? - Would
#!/bin/bash -eube any better?
From the lesson POSIX sh vs Bash: spotting and fixing bashisms
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Fresher to staff
A
/bin/shbackup job runsdump_orders | gzip > orders.gz. The oldest image's dash has no pipefail, so the guard leaves it off there. When the dump fails half-way, the job still ends with status 0 and a valid-looking archive. How do you catch the failed first stage without pipefail, and keep only complete archives?- What it tests
- Knowing what a pipeline's status reports without pipefail, a portable way to carry the first stage's status out, keeping a partial archive from replacing a good one, and proving the fix with a failing stub.
- First step
- Reproduce it before fixing it: replace
dump_orderswith a stub that prints two lines and returns 3, run the job under dash, and read both the status of the pipeline line and whatgzip -tsays aboutorders.gz. Then work out how the first stage's status can leave the pipeline, since only the last stage's status comes back by itself. - Follow-ups
-
- Why does
set -enot stop the job at the plain pipeline? - Why not read the status from
PIPESTATUS? - How do you test the fallback on a shell that has pipefail?
- What changes once every image runs a dash with pipefail?
- Why does
From the lesson What POSIX.1-2024 changed for shell scripts
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Fresher to staff
A shared POSIX sh library function sets INT and EXIT traps while it holds a lock, and must give the caller's traps back when it returns. The oldest images run dash 0.5.13.5 as
/bin/sh, which has notrap -p. How do you save and restore the caller's traps there, and show that the restore is exact?- What it tests
- Knowing what plain
trapprints and what it leaves out, how a command substitution sees the caller's traps, and how to show a restore is exact under dash and under Bash. - First step
- Start from what plain
trapshows. In dash, set only an EXIT trap and print$(trap), then set the library's INT and EXIT traps and print$(trap)again. Compare the two lists and work out what anevalof the first one would leave behind, before you write the save and restore lines. - Follow-ups
-
- What exactly does plain
trapprint for a caller that set only an EXIT trap? - Does it matter how the command substitution that saves the traps is written?
- What would
trap -pchange once every target shell has it? - How do you show that the restore is exact under dash and bash?
- What exactly does plain
From the lesson What POSIX.1-2024 changed for shell scripts
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Fresher to staff
A config step ported from Ubuntu exits 0 on a stock macOS runner, but each run leaves the old version of every edited file with -e added to its name, and its summary shows seven spaces before the host count where Ubuntu prints
hosts=3. Find both splits and make the Mac match Ubuntu.- What it tests
- Tracing a silent difference to the option parsing of one tool and the output format of another, picking forms that print the same on both systems, and a check that compares outputs and leftover files.
- First step
- Reproduce it on a stock Mac with a single edited file, list the folder after the run, and print the summary's count between brackets with printf, so each difference shows on its own before you change anything.
- Follow-ups
-
- Why did the step exit 0 although sed had misread its options?
- Which forms of the count print the same on both systems?
- What check in CI would catch both differences before the merge?
- When would installing GNU sed on the runner be the better fix?
From the lesson GNU vs BSD tools: Linux and macOS differences
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Fresher to staff
A lib.sh picks its date form: if
date -u -r 0succeeds, it wraps the BSD formdate -u -r "$1", else the GNU formdate -u -d "@$1". Macs work, but on one Ubuntu agent each call printsNo such file or directoryand the report's dates are empty. Explain why and rewrite the decision.- What it tests
- Reading an option that means different things on GNU and BSD, finding why a probe that trusts the exit status chose wrongly on one host, and writing a probe that checks what the command prints.
- First step
- Run the probe by hand on the failing agent, in the folder the job runs in, and print its exit status with
date -u -r 0; echo $?. Then run the same two commands in an empty folder and compare what each run prints. - Follow-ups
-
- How did that one agent come to differ from the others?
- Which form should the probe try first, and why?
- What should lib.sh do when neither form works?
- How would you test the probe itself on both platforms?
From the lesson GNU vs BSD tools: Linux and macOS differences
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Fresher to staff
A cleanup script with
set -umust run on the Mac's/bin/bash3.2.57: the laptops are managed and cannot install Homebrew. The team removeddeclare -Aandmapfile, and CI on Bash 5.2 is green, but a Mac run where no file is kept stops with "skipped[*]: unbound variable", status 1. Explain the cause and finish the port.- What it tests
- Tracing a failure that shows on one shell and only for one kind of input, fixing it so that it holds on both shells without giving up set -u, and proving the port on the Bash it targets.
- First step
- Run the script yourself with
/bin/bash, once with an argument that is kept and once without one, and note which run fails and on which line. Then work out what that line expands when the array has no elements, and look up whether the Bash release notes changed that case. - Follow-ups
-
- Why did the CI run on Bash 5.2 stay green?
- Which expansions would you write instead, and why not simply remove set -u?
- What else would you check before calling the port to 3.2 done?
- How would you keep the port from breaking again?
From the lesson Bash 3.2 to 5.3: versions and feature checks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Fresher to staff
A
#!/bin/bashscript probes for${v@Q}withbash -cand an output check, then picks its quoting function. On a Mac with Homebrew's Bash first on PATH, the probe finds the feature and the function fails with "bad substitution". Explain what the probe tested, fix it, and say whether a version guard would have caught it.- What it tests
- Telling the shell that runs a script from the shell a probe starts, fixing the probe so that it tests the running shell, and judging when a version guard is the more reliable check.
- First step
- Add one line near the top that prints
$BASH, the first two elements ofBASH_VERSINFOandcommand -v bash, and start the script the way its users do, as./script.sh. Compare the shell that runs the script with the shell that the probe starts. - Follow-ups
-
- What does $BASH hold, and is it still right when the script is started as sh script.sh?
- Why must the fixed probe still run in a child shell or a subshell with eval?
- Which other checks in a script read the Bash on PATH instead of the running one?
- How would you test the script so that this cannot come back?
From the lesson Bash 3.2 to 5.3: versions and feature checks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
All Bash & the POSIX Shell interview questions · The Bash & the POSIX Shell track