In bash, the here-string operator appears in commands such as `grep -q error <<< "$line"`. What exactly does `<<<` feed to the command's standard input, and how does that data differ from the string held in the variable?
answer
- one word becomes standard input
- count the bytes, not the characters
- the shell adds something at the end
- no splitting, one appended newline
basics
~20 sA here-string expands the word after <<< and supplies it as the command's standard input with a single newline appended. Bash performs parameter and command substitution on the word but no word splitting or globbing, so an unquoted variable still arrives as one intact string.
solid answer
~50 s`<<<` is a redirection, the one-line sibling of a here-document. Bash expands the word — tilde, parameter, command and arithmetic substitution, then quote removal — and hands the result to the command on stdin, **plus a trailing newline that bash adds**. That newline matters: `wc -c <<< "abc"` reports 4, not 3, and it is why line-oriented tools such as `grep`, `read` and `awk` see a proper line rather than an unterminated fragment. Word splitting and filename expansion are *not* performed on the word, so `<<< $var` delivers the same bytes as `<<< "$var"` — though I still quote it out of habit. Compared with `printf '%s\n' "$var" | cmd`, the here-string is a plain redirection rather than a pipeline, so no second process is involved. It is a bash, ksh and zsh feature, not POSIX `sh`.
code
bash · 5 linesline='alpha beta'
cat <<< "$line" | wc -c
wc -c <<< ""
read -r first rest <<< "$line"
echo "$first"go deeper
Be able to say that <<< sends a single expanded word to the command as standard input, and to use it with grep, read or jq instead of piping from echo.
Explain that bash appends a newline, and that word splitting and filename expansion are not applied to the word — so <<< $var and <<< "$var" deliver the same bytes. Know it is not POSIX.
Show where the appended newline changes an outcome, such as hashing or byte counting, and choose deliberately between a here-string, printf | cmd and a here-doc based on process count, exact bytes and which shell will run the script.
Own the shebang consequence: constructs like <<< decide whether your scripts require bash everywhere they run, including minimal container images where /bin/sh is dash or busybox. Set that expectation once, in a style rule, rather than per script.
## The construct ```bash command <<< word ``` is a *here-string*: a redirection that supplies `word`, after expansion, as `command`'s standard input. It is not a pipe, not a command, and not a file — from the command's point of view it is indistinguishable from reading a one-line file. ## What happens to the word The word goes through the shell's normal expansion machinery: tilde expansion, parameter and variable expansion (`$var`, `${var:-default}`), command substitution (`$(cmd)`), arithmetic expansion (`$((1+2))`), and finally quote removal. Two expansions are deliberately absent — **word splitting** and **filename expansion**. This is the same rule bash applies to the target word of any redirection, and it has a practical consequence: ```bash var='a b' cat <<< $var # writes: a b cat <<< "$var" # writes: a b — identical ``` The unquoted form is not the landmine it would be in an ordinary command argument. Quoting it anyway costs nothing and keeps one habit rather than two, and it protects you if the same expression is later moved into a context where splitting *does* apply. Note where the quotes live, because it is the opposite of a here-document. In `<<'EOF'` the quotes sit on the delimiter and control the *body*. In a here-string, the data is the word, so ordinary quoting rules apply to it directly: `<<< '$HOME'` is literal, `<<< "$HOME"` expands. ## The appended newline Bash terminates the here-string with a newline. This is easy to verify: ```bash wc -c <<< "abc" # 4 wc -c <<< "" # 1 ``` An empty here-string still delivers one byte. This is a feature far more often than a nuisance: line-oriented tools want their input newline-terminated, so `grep`, `read`, `sed` and `awk` all behave correctly on `<<< "$var"` where they might drop or mishandle an unterminated last line. It bites only when you are counting bytes or comparing against a checksum — for a byte-exact stream use `printf '%s' "$var" | cmd` instead. If the word itself contains newlines, they are preserved, so a here-string is perfectly capable of feeding multiple lines: ```bash block=$'one\ntwo' wc -l <<< "$block" # 2 ``` ## Typical uses **Feeding a builtin.** `read -r first rest <<< "$line"` splits a line into fields, and because a redirection creates no new process, the variables it sets are visible afterwards in the same shell — the reason people reach for it instead of `echo "$line" | read ...`. (Why the pipeline version loses the assignment is a separate topic: pipeline stages.) **Feeding a filter for a test.** `grep -q '^ERROR' <<< "$msg"` or `awk '{print $2}' <<< "$line"` avoid spawning `echo` or `printf` just to produce one line. **Feeding a tool that only reads stdin.** `jq -r .name <<< "$json"`, `base64 -d <<< "$b64"`, `md5sum <<< "$s"` — remembering, for the last one, that the appended newline is part of what gets hashed. ## Here-string versus pipe versus here-doc - A **pipe** (`printf '%s\n' "$var" | cmd`) sets up a pipeline with an extra command in it, and gives you fine control over the exact bytes, including no trailing newline. Portable everywhere. - A **here-string** is a redirection of one expanded word plus a newline: shorter, one fewer process, but bash/ksh/zsh only. - A **here-document** is the multi-line form, where quoting on the delimiter — not on the data — decides whether the body is expanded. ## Portability `<<<` is not in POSIX. A script with `#!/bin/sh` may be executed by `dash` or another minimal shell where it is a syntax error, so a here-string is one of the constructs that pins you to a `#!/usr/bin/env bash` shebang. It has been in bash since 2.05b, so on any bash you will realistically meet — including the 3.2 that ships with macOS — it is available; the constraint is the *shell*, not the version. ## Getting it wrong The two mistakes worth remembering: forgetting the appended newline when the byte count matters, and assuming `<<<` reads from a *file* named by the word. It does not — for that you want plain `< file`.
- How many bytes does `cat <<< ""` write to stdout?One — the newline bash appends. A here-string is always terminated with a newline, so even an empty word produces a single byte. When you need the exact bytes of a variable with nothing added, use `printf '%s' "$var" | cmd` instead.
- When would you prefer a here-string over piping from printf?When you want one fewer process and the receiving command is a builtin whose side effects must survive — `read -r a b <<< "$line"` sets variables in the current shell because a redirection starts no pipeline. Prefer printf when the exact bytes matter, when you need no trailing newline, or when the script must run under POSIX sh.
- Where do the quotes go to keep a here-string literal, compared with a here-document?On the data itself. A here-string is an ordinary word, so `<<< '$HOME'` is literal and `<<< "$HOME"` expands. A here-document is the reverse: the data is never quoted, and quoting the *delimiter* — `<<'EOF'` — is what turns expansion off for the whole body.
saying these in an interview costs you the question
- Claims <<< passes the string with nothing added
- Thinks an unquoted variable is word-split by <<<
- Expects <<< to open a file named by the word
- Uses <<< in a script with a /bin/sh shebang
- Says a here-string cannot carry more than one line