Unix Shell Scripting Interview Questions And Answers For Freshers
Precisely what's the distinction between $* and $@ in shell scripting? That's a question I hear almost weekly from guys fresh out of college who spent a weekend memorizing cheat sheets. The difference matters, and getting it wrong in an interview tells me you don't actually understand argument passing at all. $* expands to a single word containing all positional parameters separated by the first character of IFS, whereas $@ expands each parameter as a separate quoted word. When you're looping through arguments with for arg in $*, you lose the structure if any argument contains spaces. Use $@ instead. I had a candidate once who insisted $* was just a more concise version of $@. We ended the interview early.
What common pipeline mistake do freshers make that causes silent failures?
The pipefail option is the single most important habit you can develop, and most beginners skip it entirely. By default, Bash only checks the exit status of the last command in a pipeline. So if your grep command fails to find a match and exits with code 1, but your awk command succeeds, the entire pipeline reports success. Scripts appear to work fine until they don't, usually at 3 AM when a production process silently produces wrong output. Set set -o pipefail at the top of every script and you catch these problems immediately. I've been maintaining infrastructure scripts for years and I've seen more damage from undetected pipeline failures than from any syntax error.
How do you handle special characters in filenames inside a script?
Quote your variables. Always. "filename with spaces.txt" and "report$(date).csv" are both completely valid filenames on Unix systems. If you write a loop like for file in *.txt, you will break on the first one with spaces. The correct pattern is while IFS= read -r -d '' file; do ... done <
(find . -name "*.txt" -print0). It looks ugly at first, but it handles every edge case: spaces, newlines, leading dashes, glob characters. I spent an entire Tuesday debugging a backup script that silently truncated filenames because someone had a file called "data & export.csv" in their directory. Never let a fresh script touch production data without handling those characters properly.
What should you know about heredocs and quoting?
A heredoc with a quoted delimiter like <<'EOF' prevents variable expansion and backslash interpretation inside the body. An unquoted delimiter like <
EOF does both. This distinction trips people up constantly. If you need to embed variables inside a heredoc but also preserve literal dollar signs for something else, you have to be deliberate about which form you use. Here's a practical example I actually used recently: writing configuration files where some values come from environment variables and others must remain literal template strings. The solution is a quoted heredoc for the literal parts and concatenation with $() for the dynamic bits. Mixing the two inside one heredoc doesn't work the way you might hope.
Explain how command substitution works and when to prefer $() over backticks
Backticks `` are the original command substitution syntax inherited from the Bourne shell. They nest poorly, require escaping with backslashes, and are fragile when you combine them with other quote characters. $() is the POSIX standard and works cleanly inside double quotes, nests recursively, and is far easier to read. I still see backticks in code review submissions from junior developers, and every time I have to explain why they belong in a museum next to cron and tar options without hyphens. There is literally no reason to use backticks in new code written after 1992.
Get the Full Details

What is the purpose of the set commands set -e, set -u, and set -o errexit?
These flags turn your script from a place where mistakes go unnoticed into a place where they fail fast and visibly. set -e (or set -o errexit) exits immediately when any command returns a non-zero status. set -u treats unset variables as errors instead of silently expanding to empty strings. set -x prints each command before execution, which is invaluable for debugging but should never ship to production. I recommend starting every script with set -euo pipefail and removing -x only after you've confirmed it works. The combination catches the three categories of stupid bugs that kill more scripts than anything else: typos in variable names, unexpected command failures, and pipeline corruption.
How do you test whether a file exists, is readable, or is executable?
The test construct using single brackets [ ] and double brackets [[ ]] covers file attributes. -f checks regular files, -d checks directories, -r checks readability, -x checks executability, -s checks whether the file has nonzero size. Here's the thing most interview candidates miss: [[ ]] supports logical operators && and || directly while [ ] requires -a and -o, which behave inconsistently across shells. Use [[ ]] when you're writing Bash specifically. Stick with [ ] if portability matters. I learned this the hard way when a script that worked perfectly on Ubuntu broke on a Debian system running dash as /bin/sh. The && inside [ ] got parsed as input redirection instead of a logical AND, and the script deleted the wrong directory. It was 11 PM on a Saturday.
What distinguishes a subshell from the current shell environment?
Whenever you wrap commands in parentheses, they run in a subshell with their own copy of variables. Assignments made inside a subshell disappear when it exits. This matters because pipes create subshells for each component. If you run command1 | command2 and try to set a variable inside command1, the parent script never sees it. A common workaround is process substitution or temporary files. The while loop reading from a pipe also runs in a subshell in Bash versions before 4.2, which means variables set inside the loop body vanish afterward. If you need the results, redirect into a file or use a here-string approach instead. Candidates who can explain this demonstrate actual understanding rather than rote memorization.
How would you write a function that returns multiple values?
Shell functions don't return arrays or objects the way languages like Python do. The traditional approach uses echo to output values and command substitution to capture them, or you use global variables that the function sets. Modern Bash supports nameref variables with declare -n, which lets you pass variable names by reference and modify them directly from the function. Here's a practical pattern: function get_stats() { local count=$1; local sum=0; for i in $(seq 1 "$count"); do sum=$((sum + RANDOM)); done; echo "$count $sum"; }. Then capture with read count sum <<
"$(get_stats 100)". It's not elegant, but it works reliably across versions.
What are exit codes and why do they matter in interview scenarios?
Every Unix command exits with a numeric code between 0 and 255. Zero means success. Non-zero means failure, and the specific number often indicates the type of error. interviewers ask about this because exit codes are how scripts communicate with orchestrators, cron jobs, and monitoring systems. If your script handles errors poorly, the upstream system never knows anything went wrong. I always check $? after critical operations or use set -e to automate the check. A script that ignores exit codes is a script that pretends failures didn't happen, and that's worse than a script that crashes visibly.

How do you debug a shell script that's producing unexpected output?
Start with bash -x script.sh to trace every command as it executes. The output shows variable expansion in real time, which catches issues like empty variables, incorrect word splitting, and unexpected quoting behavior. If the script runs slowly, add timestamps around suspect sections or use bash -v for raw line-by-line output without expansion. For persistent debugging, consider using the trap command to catch ERR signals and print context. I once spent three hours tracking down a bug that turned out to be a carriage return in a here-document on a file that had been edited on Windows. The trap with err handler would have shown me the problem in seconds.
What is the difference between source and the dot operator, and when do you use them?
They are identical. source and . are the same command in Bash. Both execute a file in the current shell environment rather than spawning a subprocess. This matters for loading configuration, setting environment variables, or defining functions that need to persist after the include completes. If you execute a script with ./script.sh or bash script.sh instead of sourcing it, the changes disappear when that script exits. I use this constantly for modular scripts where I split logic into separate files and source them at runtime. It keeps the codebase organized without the overhead of multiple processes.