skip to content

Variables and Expansion

Everything that happens between the text you type and the argument list the command finally receives: quoting, parameter expansion, arrays, word splitting, and globbing. Most of the bash bugs interviewers ask about — a filename with spaces, an empty variable that eats an argument — are expansion bugs, which makes this the densest part of the language.

part ofBashoverview, primer and where to startread it →
on this pageshow

questions

page 2 of 2

A colleague adds `IFS=$'\n\t'` near the top of a shared bash script, saying it makes the script safer. What does that global change actually buy, what does it fail to protect against, and what can it break?

level: seniorimportance: should knowfreq 38%

basics

~20 s

It removes the space from the separator set, so unquoted expansions no longer split on spaces — only on newlines and tabs. It does not stop globbing, does not make quoting unnecessary, and silently changes any code that relied on space splitting, including sourced libraries.

open as a page

In bash with `v=''`, the function `count() { echo $#; }` prints different numbers for `count $v` and `count "$v"`. What does each print, and why?

level: juniorimportance: nice to knowfreq 42%

basics

~20 s

count $v prints 0 and count "$v" prints 1. An unquoted empty expansion is split on IFS into zero words, so no argument is passed at all, while double quotes force it to become exactly one argument, an empty string.

open as a page

In bash, after `arr=(a b c); unset 'arr[1]'`, what do `${#arr[@]}` and `${!arr[@]}` report, and why is a C-style loop from 0 to `${#arr[@]}-1` the wrong way to iterate an array?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

Bash arrays are sparse: unset removes index 1 without renumbering, so ${#arr[@]} is 2 while the live indices from ${!arr[@]} are 0 and 2. A counter loop bounded by the count would read index 1, which no longer exists, and never reach index 2.

open as a page

A bash associative array `declare -A opts` may legitimately hold empty-string values. How do you test whether a given key is present, as opposed to present-but-empty?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Use [[ -v opts[key] ]], which is true when the key exists whatever its value (bash 4.3+ accepts array subscripts here). The pre-4.3 equivalent is [[ -n ${opts[key]+x} ]]. Testing [[ -n ${opts[key]} ]] conflates absent with empty.

open as a page

In bash, `$RANDOM` and `$SECONDS` return a different value each time they are read. What do these shell-maintained variables give you, and what should `$RANDOM` not be used for?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Each reference to $RANDOM yields a new pseudo-random integer between 0 and 32767, and $SECONDS counts whole seconds since the shell started. $RANDOM is a seeded pseudo-random generator, so it must not be used for anything security-sensitive.

open as a page

What does the pattern `**` expand to in bash, what has to be enabled for it to be recursive, and how does `for f in **/*.js` differ from `find . -name '*.js'`?

level: middleimportance: nice to knowfreq 34%

basics

~20 s

Without shopt -s globstar, ** behaves like a single * and stays inside one directory level. With globstar on (bash 4.0+), **/ matches zero or more directories, so **/*.js recurses. Unlike find, it still skips hidden entries and expands entirely in memory.

open as a page

In a bash script you need one string containing a literal single quote and another containing a real tab character. Single quotes have no escape character — how do you build each of these, and why is `echo -e` not the answer?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Close, escape, reopen: 'it'\''s' produces it's because single quotes cannot contain an escaped quote. For control characters use ANSI-C quoting, $'\t', which bash converts to a real tab before the command ever runs; printf is the portable alternative to echo -e.

open as a page

Since bash expands a command line only once, when does a script genuinely need `eval` to force a second pass, and which bash features let you avoid reaching for it?

level: seniorimportance: nice to knowfreq 34%

basics

~20 s

eval re-parses its argument as a fresh command line, giving a second full round of expansion. It is genuinely needed only when the command's structure, not just its arguments, is computed at run time — for example running the shell assignments a tool prints. Arrays, printf -v and namerefs cover most other cases.

open as a page

A bash deploy script holds an environment name in $ENV (PROD or STAGING) and reads configuration from variables named PROD_HOST and STAGING_HOST. How does bash's indirect expansion ${!name} let you read the right one, and how does it compare with using eval?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Build the variable name in a string, then expand it indirectly: key="${ENV}_HOST"; host="${!key}". Bash reads the variable whose name is the value of key. Unlike eval, it substitutes only a value and never executes the name as code.

open as a page

showing 31–39 of 39