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?
answer
- there is no escape inside them
- close, escape, reopen
- adjacent fragments form one word
- a third quote form handles escapes
- one common echo flag is unspecified
basics
~20 sClose, 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.
solid answer
~50 sInside single quotes nothing is special, including the backslash, so a single quote simply ends the string and there is no way to escape one. The standard workaround is to close the quote, emit an escaped quote outside it, and reopen — `'it'\''s fine'` — which the shell concatenates into one word; using double quotes (`"it's fine"`) is simpler when the text has no `$` or backtick. For control characters bash offers ANSI-C quoting: `$'...'` processes C-style escapes at parse time, so `$'\t'` is a real tab, `$'\n'` a newline, and `IFS=$' \t\n'` restores the default field separators readably. `echo -e` is the wrong reflex because `echo`'s handling of `-e` and backslashes varies between bash builtin, `/bin/sh` and `/bin/echo`; `printf '%s\t%s\n' "$a" "$b"` is specified and portable. Note that `$'...'` itself is a bash extension, not POSIX sh.
code
bash · 6 linesecho 'it'\''s fine' # it's fine (close, escape, reopen)
echo "it's fine" # it's fine (simpler when no $ or backtick)
tab=$'\t'
printf 'a%sb\n' "$tab" # a<TAB>b
IFS=$' \t\n' # the default IFS, written readablygo deeper
Know the two shapes: use double quotes when the text contains an apostrophe, and remember that \t inside ordinary quotes is not a tab. printf is the command that turns escapes into real characters.
Explain why single quotes admit no escape at all, show the close-escape-reopen construction and that adjacent fragments concatenate into one word, and use $'...' for control characters such as IFS=$' \t\n'.
Demonstrate the portability judgment — echo -e is unspecified and differs between bash and dash, $'...' is a bashism, and printf '%q' is the correct tool when a value must survive being re-parsed by another shell.
Push the question upstream: strings that require this much quoting usually signal a script assembling code for another interpreter, and passing structured data as arguments or a file is a more durable design than perfecting the escaping.
## Why single quotes cannot hold a single quote Bash's single-quote rule is absolute: between `'` and the next `'`, every character stands for itself. There is no escape character at all, so `'it\'s'` does not do what it looks like. Bash reads `'it\'` as a complete quoted string containing `it\`, then sees a bare `s`, then an opening quote that swallows the rest of the line — usually producing a hung prompt waiting for the closing quote, or a baffling error. The fix relies on a fact that is easy to miss: **adjacent quoted and unquoted fragments with no whitespace between them form a single word.** `'a'b'c'` is the one word `abc`. So to embed a quote you leave the single-quoted region, produce a quote by another means, and re-enter: ```bash echo 'it'\''s fine' # it's fine echo 'it'"'"'s fine' # it's fine - same trick with a double-quoted quote ``` Read `'it'\''s fine'` as four pieces: `'it'` + `\'` + `'s fine'`. It is famously unreadable, which is why the practical advice is different: if the text contains an apostrophe but no `$`, no backtick and no backslash, just use double quotes — `"it's fine"`. Reach for the close-escape-reopen dance only when the text also contains characters that double quotes would expand. This matters most when generating shell code for another shell to run. `printf '%q'` exists precisely for that: it prints its argument requoted so that re-parsing it as shell input yields the original string, handling quotes, spaces and control characters for you. ## ANSI-C quoting for control characters Neither quote form interprets backslash escapes: `echo "a\tb"` and `echo 'a\tb'` both print `a\tb`, because `\t` means nothing to the shell. The third quoting form does interpret them. `$'...'` is **ANSI-C quoting** — bash replaces the C-style escape sequences at parse time and the result behaves like a single-quoted string thereafter (no expansion of `$var` inside it). ```bash tab=$'\t' nl=$'\n' printf 'a%sb\n' "$tab" # a<TAB>b IFS=$' \t\n' # the default IFS, written explicitly ``` Supported escapes include `\n`, `\t`, `\r`, `\\`, `\'`, `\"`, `\e` (escape), `\a`, and numeric forms `\nnn` (octal) and `\xHH` (hex). Unicode `\uHHHH` and `\UHHHHHHHH` were added in bash 4.2. Because the value is produced at parse time, `$'...'` is the readable way to put a control character into a variable, a delimiter, or an `IFS` assignment — far clearer than embedding a literal tab that no reviewer can see. It is also, importantly, a **bash extension**. `$'...'` is supported by bash, ksh and zsh but is not in POSIX sh; a script with a `#!/bin/sh` shebang running under dash will treat `$'\t'` as a `$` followed by a quoted `\t`. If you need portability there, use `printf` or a literal character produced by `tab=$(printf '\t')`. ## Why not echo -e `echo -e '\t'` works in bash and is tempting, but `echo` is one of the least portable commands in the shell: - bash's builtin `echo` accepts `-e` unless the shell was built or invoked with different defaults; the `xpg_echo` shell option changes its behaviour. - `/bin/sh` on Debian is dash, whose `echo` interprets backslash escapes *without* `-e` and prints `-e` as literal text if you pass it. - `/bin/echo` and the shell builtin do not agree with each other on every system. - Any string beginning with `-` may or may not be treated as an option. POSIX explicitly leaves this unspecified. `printf` is specified, behaves the same everywhere, and separates format from data: ```bash printf '%s\t%s\n' "$user" "$id" # correct: data goes through %s printf "$user\n" # wrong: user data used as a format string ``` That last line is the mistake to avoid — a value containing `%s` or `\n` would be interpreted as formatting. Data always belongs in an argument, never in the format string. ## Putting it together Single quotes for literal text, double quotes for text that needs expansion, `$'...'` when you need a control character in the value itself, and `printf` whenever escape sequences must reach the output. Those four choices cover essentially every string-building situation a script encounters.
- Are variables expanded inside `$'...'`?No. Bash processes the C-style escape sequences at parse time and the result behaves like a single-quoted string, so `$'$HOME'` yields the literal five characters `$HOME`. If you need both an escape and an expansion, build the value in pieces: `sep=$'\t'; line="$a$sep$b"`.
- How do you safely embed an arbitrary value into a command string that another shell will re-parse?Use `printf '%q'`, which prints the argument requoted so that re-parsing it as shell input reproduces the original bytes, including quotes, spaces and newlines. Hand-rolling the quoting is where mistakes creep in. Better still, avoid the second parse entirely and pass the value as a normal argument.
- What is the portable way to get a tab into a variable in a `#!/bin/sh` script?`tab=$(printf '\t')`. The `$'\t'` form is a bash, ksh and zsh extension that dash does not implement, so under `/bin/sh` on Debian it would produce a literal backslash-t. `printf` is specified by POSIX and behaves identically across shells.
saying these in an interview costs you the question
- Thinks a backslash escapes a quote inside single quotes
- Expects \t inside double quotes to become a tab
- Reaches for echo -e as the portable escape mechanism
- Assumes $'...' works in any POSIX sh script
- Passes user data as printf's format string