check values with <<< directly, no temp file involved
A here-string sends a single configuration value straight to the standard input of a validation command like jq or grep -P using the <<< operator, without ever creating a temp file. For small, repeated checks inside loops, that is both faster and simpler to clean up than any alternative involving a temp file or an extra echo subshell.
Table of Contents
- 1. What a here-string is and why it fits validation so well
- 2. Basic syntax: command <<< "$value" instead of a temp file
- 3. Validating configuration values with jq and a here-string
- 4. Regex validation of single values with grep -P
- 5. Advantages for repeated checks inside loops
- 6. Limits of the here-string approach: large and multi-line data
- 7. Error handling: evaluating exit codes and set -e behavior
- 8. Building validation functions with a here-string parameter
- 9. Security and quoting: special characters in configuration values
- 10. Summary
- 11. FAQ
1. What a here-string is and why it fits validation so well
A here-string uses the <<< operator to send exactly one string as standard input to a command, for example jq '.port' <<< "$config_value". It differs both from a here-doc <<EOF ... EOF, meant for multi-line literal text blocks, and from a classic pipe |, which forwards the output of a previous command rather than a single variable.
That is exactly the use case configuration validation needs: a single value already sitting in a Bash variable has to reach a check command without first being written to a file. Routing it through a temp file brings unnecessary disk I/O, an explicit cleanup obligation, and an extra failure surface that a here-string avoids from the start.
2. Basic syntax: command <<< "$value" instead of a temp file
The obvious alternative to a here-string is often echo "$value" | jq ..., which looks functionally almost identical but internally spins up an extra process for echo plus a real pipe between two processes. The here-string skips the echo process entirely, because Bash forwards the string to the target command itself, either through an anonymous temp file or directly through a file descriptor.
Correct quoting matters most: <<< $value without quotes is subject to the shell's word splitting and glob expansion, which for values containing spaces or special characters produces wrong or multiple arguments. <<< "$value" with quotes transmits the value exactly as stored in the variable, spaces included.
#!/usr/bin/env bash
set -euo pipefail
value='{"port": 8080}'
# Pipe variant: spawns an extra echo process plus a real pipe
echo "$value" | jq -e '.port | numbers' > /dev/null
# Here-string variant: no extra echo process needed
jq -e '.port | numbers' <<< "$value" > /dev/null
echo "Both checks passed"
3. Validating configuration values with jq and a here-string
For JSON configuration values, jq combined with a here-string is particularly convenient, since type and value checks can be expressed directly in a jq expression. A call like jq -e '.port | type == "number"' <<< "$json_value" checks whether the port field is actually a number, without Bash itself ever having to parse JSON.
The -e flag matters here: it makes jq return a non-zero exit code whenever the jq expression evaluates to false or null. That lets the result be used directly in a Bash if condition, without ever having to parse jq's textual output.
#!/usr/bin/env bash
set -euo pipefail
configs=(
'{"host": "db01", "port": 5432}'
'{"host": "db02", "port": "not-a-number"}'
)
for cfg in "${configs[@]}"; do
if jq -e '.port | type == "number"' <<< "$cfg" > /dev/null; then
echo "OK: $cfg"
else
echo "INVALID port in: $cfg" >&2
fi
done
4. Regex validation of single values with grep -P
For simple text values, such as a version number or an email address in a config file, grep -P -q <<< "$value" with a PCRE pattern is often enough. The -P flag enables Perl-compatible regular expressions, considerably more powerful than the POSIX baseline of grep, allowing constructs like non-greedy quantifiers or lookaheads.
The -q flag suppresses all text output and returns just the exit code, exactly right for pure validation purposes. Combined with a here-string, this produces a very compact check line that needs neither an intermediate variable for the output nor a temp file.
#!/usr/bin/env bash
set -euo pipefail
is_semver() {
grep -Pq '^\d+\.\d+\.\d+$' <<< "$1"
}
for version in "2.4.8" "2.4" "8.4.1"; do
if is_semver "$version"; then
echo "valid semver: $version"
else
echo "invalid semver: $version" >&2
fi
done
5. Advantages for repeated checks inside loops
The real advantage of a here-string shows up once many values get checked in a row inside a loop. The echo "$value" | cmd variant spawns two processes plus a real pipe between them on every iteration, while a here-string only starts the one process for the actual check command, since Bash forwards the value itself.
For a loop over hundreds of configuration values, for example validating every environment variable of a deployment script before the actual rollout, that difference adds up noticeably. Fewer spawned processes mean less overhead from fork and exec, an effect that is especially measurable in CI pipelines with a tight time budget.
#!/usr/bin/env bash
set -euo pipefail
declare -A env_vars=(
[DB_PORT]="5432"
[APP_PORT]="not-a-port"
[WORKER_COUNT]="4"
)
for key in "${!env_vars[@]}"; do
value="${env_vars[$key]}"
if grep -Pq '^\d+$' <<< "$value"; then
echo "OK: $key=$value"
else
echo "INVALID: $key=$value is not numeric" >&2
fi
done
6. Limits of the here-string approach: large and multi-line data
A here-string suits a single, reasonably sized value best. Bash often backs the content internally with an anonymous temp file inside $TMPDIR, which for very large strings, say an entire multi-megabyte configuration document, offers no real advantage over a real file anymore and does not improve readability either.
For multi-line values, such as a YAML block that needs to be checked verbatim with embedded line breaks, a here-doc with <<EOF ... EOF is often the more readable choice, since line breaks can be written naturally inside it rather than reconstructed with $'\n' escapes inside a Bash variable. For single-line values, the here-string keeps the clear advantage.
7. Error handling: evaluating exit codes and set -e behavior
The exit code of the validation command behind a here-string behaves like that of any other command: it can be evaluated directly in if, && or ||. Inside an if condition, a failing call does not abort the script even with set -e active, because Bash deliberately exempts commands inside a condition from the errexit rule.
Outside a condition, for example as a standalone line grep -Pq '...' <<< "$value", a failing call with set -e active would immediately terminate the whole script. For validation logic meant to handle the failure itself rather than stop the entire script, the call therefore always belongs inside an explicit if or behind a ||.
8. Building validation functions with a here-string parameter
A reusable validation function takes the value under test as a parameter and forwards it internally to the actual check command via a here-string, for example validate_port() { grep -Pq '^\d{1,5}$' <<< "$1"; }. The function's return value automatically matches the exit code of its last command, so the function itself stays usable like a boolean inside if conditions.
Several such functions can be combined into a table-driven check that maps configuration keys onto the matching validation function. That keeps the actual check logic per value type bundled in one place, and makes it easy to add new configuration keys along with their appropriate validation.
#!/usr/bin/env bash
set -euo pipefail
validate_port() { grep -Pq '^\d{1,5}$' <<< "$1"; }
validate_host() { grep -Pq '^[a-zA-Z0-9.-]+$' <<< "$1"; }
declare -A validators=([DB_PORT]=validate_port [DB_HOST]=validate_host)
declare -A values=([DB_PORT]="5432" [DB_HOST]="db01.internal")
for key in "${!validators[@]}"; do
fn="${validators[$key]}"
if "$fn" "${values[$key]}"; then
echo "OK: $key"
else
echo "INVALID: $key" >&2
fi
done
9. Security and quoting: special characters in configuration values
Missing quotes on a here-string are the most common source of bugs: <<< $value without quotes lets the shell interpret spaces, asterisks and other glob characters inside the value before the check command ever sees it. A configuration value containing an embedded space would silently split into multiple words, and the validation returns a wrong result.
Unlike echo "$value" | cmd, a correctly quoted here-string handles the value consistently, including any trailing newline it may contain, which makes behavior more predictable. Embedded NUL bytes are not supported, since Bash strings always end at a NUL byte, though that rarely matters for plain configuration values in practice.
| Technique | Extra process | Temp file | Ideal for |
|---|---|---|---|
Here-string (<<<) |
No | No | Single value, repeated inside loops |
Pipe with echo (echo | cmd) |
Yes, echo | No | One-off ad-hoc check in a terminal |
Here-doc (<<EOF) |
No | No | Multi-line, literal text blocks |
| Temp file | No | Yes | Large documents, reused multiple times |
Mironsoft
Shell automation, DevOps tooling and deployment infrastructure
Shell scripts that hold up in production?
We review existing Bash scripts, spot fragile patterns and replace them with robust Bash patterns: complete error handling, logging and safe parallelization for your deployment stack.
Code Review
ShellCheck analysis and manual review for critical Bash pattern violations.
Refactoring
Retrofitting error handling, logging and safe file operations.
CI Integration
Wiring ShellCheck and BATS into pipelines and building regression tests.
10. Summary
Here-Strings for Configuration Validation: The Essentials at a Glance
Core idea
<<< sends a single value as stdin to a command, no echo subshell and no temp file involved.
jq and grep -P
jq -e checks type and value in JSON configuration, grep -Pq validates text patterns silently, both return a usable exit code.
Loop performance
Fewer spawned processes per iteration than echo | cmd, noticeable across hundreds of configuration values in CI pipelines.
Limits
For very large or multi-line data, a here-doc or a real file is often the more readable and sensible choice.