Bash & the POSIX Shell Module 6
Redirection, pipes and substitution interview questions
Interview questions on redirection, pipes and substitution: 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
Name the three standard streams with their numbers, and say what 2>&1 does.
Standard input is descriptor 0, where a command reads, by default from the keyboard. Standard output is descriptor 1, for results, and standard error is descriptor 2, for warnings and errors; in a terminal both go to the screen. 2>&1 makes descriptor 2 a copy of descriptor 1 as it points at that moment, so errors go wherever results are going. Because redirections run from left to right, cmd > out.txt 2>&1 puts both streams in out.txt, while cmd 2>&1 > out.txt sends errors to the old standard output, the terminal or a pipe, and only the results to the file. Piped into sed, the second form let the warning alone through the pipe.
From the lesson Redirection: stdin, stdout, stderr, /dev/null
-
Fresher to mid
What status does a pipeline return, how do you see every stage's status, and what does pipefail change?
A pipeline's status is its last command's: false | true returns 0 because true came last. PIPESTATUS holds every stage's status as an array, ${PIPESTATUS[0]} for the first stage; copy it on the same line, because the next command replaces it. With set -o pipefail, the pipeline returns the status of the rightmost command that failed, or 0 when all of them succeeded, so false | true returns 1. pipefail has been in Bash since 3.0 and is part of POSIX.1-2024.
From the lesson Pipelines, PIPESTATUS and pipefail
-
Fresher to mid
How do <<EOF, <<'EOF' and <<-EOF differ?
-
Fresher to mid
What does exec do when it is given redirections and no command, and how do exec 3>&1 and exec 3> trace.log differ?
Without a command, exec runs no program: it applies its redirections to the shell that runs it, and they stay in force for every later command of the script until another exec changes them. exec 3> trace.log opens descriptor 3 on trace.log, emptied first, so echo text >&3 writes into the file. exec 3>&1 opens no file: it makes 3 a copy of descriptor 1 as it points at that moment, usually to save standard output before moving it, so that exec 1>&3 3>&- can restore it later. Every program the script starts inherits both kinds, so close a descriptor with exec 3>&- once it is no longer needed; a write after the close fails with "Bad file descriptor".
From the lesson File descriptors and exec redirections
-
Fresher to mid
What happens to trailing newlines in command substitution, and how would you keep them?
Bash deletes every newline at the end of the captured output, however many there are; newlines inside the output stay. A file holding alpha and beta followed by two blank lines, 13 bytes, is captured by out=$(cat lines.txt) as 10 bytes. To keep them, print a sentinel character after the command inside the same substitution and remove it afterwards: out=$(cat lines.txt; echo .), then out=${out%.}. Bash now deletes only the newline after the dot, the file's own newlines are no longer at the end, and removing the dot leaves all 13 bytes. Quote the variable when you use it, as "$out", or word splitting drops the newlines again.
From the lesson Command and process substitution
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Fresher to mid
A nightly cron entry runs /opt/jobs/sync.sh 2>&1 >> /var/log/sync.log. The log fills with progress lines, but on the night the remote host was down it holds no error at all, and the error text arrived in the job's cron mail instead. Explain where each stream went, and fix the line.
- What it tests
- Applying the left-to-right order of redirections to a scheduled job, knowing what cron does with output that is not redirected, and choosing >> file 2>&1.
- First step
- Follow the two descriptors. Under cron both start at the output cron collects and mails. 2>&1 copies that, so the errors keep going to the mail, and >> /var/log/sync.log then moves only descriptor 1 to the log. Swap the order: /opt/jobs/sync.sh >> /var/log/sync.log 2>&1.
- Follow-ups
-
- Why did the same line look fine when you ran it by hand in a terminal?
- How would you keep a separate file of the errors as well as the combined log?
- What would ShellCheck have said about the line?
From the lesson Redirection: stdin, stdout, stderr, /dev/null
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 4 minutes
-
Mid to senior
setup.sh begins with #!/bin/sh and set -e, then runs ./migrate.sh &> migrate.log and ./seed.sh. Run with bash on a laptop, migrate.log fills. The build server runs ./setup.sh, and its sh is dash: migrate.log stays empty, the migration's output lands in the job console, and seed.sh sometimes fails on missing tables. Explain all three, and fix it.
- What it tests
- Knowing that &> is Bash syntax, how dash splits it into & and a bare redirection, what that does to the order of the steps and to set -e, and how to catch it before it runs.
- First step
- Read the line as dash does: ./migrate.sh & starts the migration in the background, and > migrate.log is a command of its own that only empties the file. The output still goes to the console, and seed.sh starts at once, while the migration is still running. Write ./migrate.sh > migrate.log 2>&1.
- Follow-ups
-
- Why did set -e not stop the script when a migration failed?
- How would you catch this before the script reaches the server?
- Would changing the first line to #!/bin/bash be a better fix?
From the lesson Redirection: stdin, stdout, stderr, /dev/null
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A monitor runs under set -o pipefail and pages when if dump_journal | grep -q 'disk full' succeeds. On quiet days it pages as it should. On a busy day the dump was 100,000 lines long with 'disk full' near the top, and no page fired. Why, and how would you fix the check?
- What it tests
- Seeing that grep -q stops reading at the first match, that the writer then dies of SIGPIPE, that pipefail turns its 141 into a failed if, and fixing it without hiding a real failure of the dump.
- First step
- grep -q exits as soon as it finds the line. dump_journal, still writing, gets SIGPIPE and exits 141, and under pipefail that is the pipeline's status, so the if takes the else branch although the line was found. A small dump fits into the pipe before grep exits, which is why quiet days worked.
- Follow-ups
-
- Why did the same check work on quiet days?
- Would grep without -q, with its output sent to /dev/null, fix it, and at what cost?
- How would you still notice a dump_journal that really failed?
From the lesson Pipelines, PIPESTATUS and pipefail
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A clean-up step runs sort users.txt | uniq > users.txt to remove duplicate lines. Afterwards users.txt is empty, every time, although sort and uniq both exit 0. What happened, and how would you remove the duplicates in place safely?
- What it tests
- Knowing that the stages of a pipeline start together, that > empties its file when the shell sets up the last stage, and replacing the file only after the whole pipeline has succeeded.
- First step
- The shell starts both stages at once and opens users.txt for uniq's > as it starts the last stage; > empties the file, so sort reads 0 bytes and uniq writes nothing. Write to a new file and rename it over users.txt only when the pipeline succeeded, or let sort write the file itself with sort -u -o users.txt users.txt.
- Follow-ups
-
- Why does sort -o users.txt users.txt not have the same problem?
- Why does the temporary-file version need pipefail?
- Would >> instead of > fix it?
From the lesson Pipelines, PIPESTATUS and pipefail
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
deploy.sh writes app.conf with cat > app.conf <<EOF, and someone typed the database password into the body by hand: db_password=Tr0ub4$$dor. After the deploy the database rejects the password, and app.conf shows Tr0ub4, then a five-digit number, then dor. Explain what Bash did, and fix the script.
- What it tests
- Knowing that an unquoted here-document expands the dollar signs of its own text, that $$ is the process ID of the shell, choosing <<'EOF' or \$ escapes, and moving the secret out of the script.
- First step
- The body of <<EOF is expanded like a double-quoted string, so $$ in the typed password became the process ID of the shell running deploy.sh, a different number on every run. Quote the delimiter, <<'EOF', so the body is written as typed; when other lines must still expand, write each literal dollar sign as \$.
- Follow-ups
-
- What would the body line db_password=S3cret$tash have produced?
- The password now comes from a file, read into $password and written as db_password=$password. Can a $ in that file do the same?
- How would you stop a value with a newline from adding a line to app.conf?
From the lesson Here-documents and here-strings
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
deploy.sh writes nginx.conf with cat > nginx.conf <<-EOF, its body and closing EOF indented with tabs. An editor then replaced every tab with four spaces. The next deploy printed a warning that a here-document was delimited by end-of-file, nginx.conf gained four spaces on every line, and the script's last lines never ran. Explain each symptom, and prevent a repeat.
- What it tests
- Knowing that <<- strips tabs only, so the closing line no longer matches, that Bash then takes the rest of the file as the body, and that ShellCheck's SC1040 catches the mistake where bash -n does not.
- First step
- <<- strips leading tabs, never spaces. The body lines keep their four spaces, and the closing line now reads four spaces and EOF, which is not the delimiter, so Bash reads to the end of the file looking for it: the script's remaining lines become part of nginx.conf instead of running, and Bash warns about the missing delimiter.
- Follow-ups
-
- Would bash -n deploy.sh in the build have caught it?
- What changes when the here-document sits inside a function?
- What is the smallest fix that keeps the body indented?
From the lesson Here-documents and here-strings
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Cron runs maint.sh >> maint.log 2>&1 every night. Its prune function starts with exec 4>&1 > /dev/null to hide rm's chatter and ends with exec 1>&4 4>&-. On nights with nothing to prune, maint.log keeps the error lines but loses every normal line after prune. Explain why, and fix it.
- What it tests
- Knowing that an exec redirection changes the whole shell until changed again, finding the path that skips the restore, and moving the redirection onto the call, where Bash undoes it by itself.
- First step
- exec 4>&1 > /dev/null changes the descriptors of the script's shell, not of the function. On a quiet night prune returns early, before exec 1>&4 4>&-, so descriptor 1 stays on /dev/null for the rest of the run, while descriptor 2 still points at the log, which is why the errors survive.
- Follow-ups
-
- Why do the lines printed before prune still reach the log?
- How would you rewrite prune so that no path can skip the restore?
- Should rm's error messages be hidden too?
From the lesson File descriptors and exec redirections
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
export.sh prints 3,000 CSV rows on standard output and runs as ./export.sh | gzip > rows.csv.gz. Someone added echo "exported $n rows" every 1,000 rows, and the decompressed file now has 3,003 lines. Where should the progress go so that the data stays clean, and how does the person running the export still see it?
- What it tests
- Keeping data on standard output and messages on standard error, knowing that a pipe carries descriptor 1 only, and checking the fix by counting the rows that reach the file.
- First step
- The pipe to gzip carries descriptor 1 only, so whatever is echoed there becomes data. Send the progress to standard error with >&2, or to descriptor 3 opened as a copy of it with exec 3>&2; the screen still shows it, because only descriptor 1 goes into the pipe. Then check: gzip -dc rows.csv.gz | wc -l must say 3,000.
- Follow-ups
-
- Why not write the progress to /dev/tty instead?
- How would the script print progress only when a person is watching?
- Why copy standard error to descriptor 3 instead of writing to 2 directly?
From the lesson File descriptors and exec redirections
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
release.sh starts with set -e, runs echo "tagging release-$(cat VERSION)" and then writes release-$(cat VERSION) to tag.txt. One night VERSION was missing: cat printed an error, yet the script ended with status 0 and tagged the build release-. Why did set -e not stop it, and how would you fix the script?
- What it tests
- Knowing that set -e reacts to the status of a command, not of a substitution inside its arguments, that only an assignment passes a substitution's status on, and moving the capture onto its own checked line.
- First step
- set -e looks at the status of each command. The substitution is part of echo's argument: cat fails inside it, but the line's status is echo's 0, so the script goes on with an empty version. Capture first, with version=$(cat VERSION) on its own line: that assignment takes cat's status, and set -e stops the script there.
- Follow-ups
-
- Would local v=$(cat VERSION) inside a function have stopped the script?
- How would you also refuse a VERSION file that exists but is empty?
- Why did the printf line that wrote tag.txt not stop the script either?
From the lesson Command and process substitution
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
added.sh runs under set -euo pipefail and reports new accounts with comm -13 <(sort old-users.txt) <(sort "$today") > added.txt. The morning today's export was missing, sort printed an error, yet the script said "added: 0 accounts" and exited 0. Why did none of the three options stop it, and how would you make it fail?
- What it tests
- Knowing that the shell checks comm's status and never a process substitution's, that pipefail covers pipelines only, and making the script check its inputs or wait for each substitution.
- First step
- The failure happened inside <( ), and Bash does not fold a process substitution's status into the command that reads it. comm compared the old list against an empty input, found no new line and exited 0, so set -e and pipefail saw success. Check both inputs before the comparison, or wait for the substitution: wait $! returned sort's failure here.
- Follow-ups
-
- Would wait $! also catch a missing old-users.txt?
- Why did pipefail not help, when the sorts write into pipes?
- How would you wait for both sorts?
From the lesson Command and process substitution
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
All Bash & the POSIX Shell interview questions · The Bash & the POSIX Shell track