/errors/bash/ambiguous-redirect
bash: $file: ambiguous redirect
- tool
- Bash
- exit code
- 1
- versions
- bash 4.x, bash 5.x
- os
- linux, macos, windows
ambiguous redirect means bash expanded the target of >, >> or < to something other than exactly one word. That is either nothing (the variable is empty or unset) or several words (it contains spaces and is unquoted, or it is a glob with many matches). Quote the variable and check that it is set.
Reproduce it
An unset variable expands to nothing:
bash -c 'echo hi > $nofile'Output · exit 1
bash: line 1: $nofile: ambiguous redirect
An unquoted value with a space splits into two words:
bash -c 'f="a b.txt"; echo hi > $f'Output · exit 1
bash: line 1: $f: ambiguous redirect
Fixes
Quote the target so it stays one word, and fail early when the variable is unset:
bash -c 'f="a b.txt"; echo hi > "$f"; cat "$f"'Output
hi
bash -c 'unset f; echo hi > "${f:?f is not set}"'Output · exit 127
bash: line 1: f: f is not set
Other ways to trigger it
The same check applies to every expansion in a redirection target, not only plain variables. A glob that matches two files, an empty command substitution and an array all fail, and the message repeats the target as written in the script, which is the fastest clue to the line at fault:
touch a.log b.log
bash -c 'echo hi > *.log'
bash -c 'echo hi > $(ls missing-dir 2>/dev/null)'
bash -c 'list=(one two); echo hi > ${list[@]}'
bash -c 'out=""; echo hi > $out; echo "script continues, status was $?"'Output
bash: line 1: *.log: ambiguous redirect
bash: line 1: $(ls missing-dir 2> /dev/null): ambiguous redirect
bash: line 1: ${list[@]}: ambiguous redirect
bash: line 1: $out: ambiguous redirect
script continues, status was 1The last line shows that the failure is not fatal: bash reports the error, skips that command, and runs the next one. In a script without set -e, check $? or enable set -euo pipefail (see set -euo pipefail) so the script stops instead of continuing with missing output.
How to find the culprit
Run the script with tracing, bash -x script.sh, or put set -x above the failing line. The trace prints every command after expansion, including the assignments that set the variable, so you can see whether it ended up empty or with spaces just before the failing line. For a variable you expect to be set by the caller, add a guard at the top of the script, : "${OUTPUT:?set OUTPUT to the output file}", which fails once with a clear message rather than at every redirection.
Bash versus sh
The message comes from bash. In POSIX mode (bash --posix, or bash running as sh) a non-interactive shell does not expand globs in redirection targets, so > *.log creates a file literally named *.log instead of failing. Other shells word their errors differently, so search for the text your shell prints when the script runs under /bin/sh on Debian or Ubuntu, where that is dash.
Prevention
- Always quote expansions used as file names:
> "$out",< "$in",2> "$log". - Build paths from parts that cannot be empty, for example
"${dir:?}/report.txt". - When you need to write to several files, loop:
for f in *.log; do echo hi > "$f"; done, or usetee file1 file2. - Run ShellCheck in CI; it warns about unquoted variables in redirections before the script runs.
FAQ
Does ambiguous redirect stop the script?
No. Only the command with the bad redirection fails, with status 1, and the script continues on the next line unless set -e is active. Later commands may then work on a file that was never written.
Why do I get No such file or directory after quoting the variable?
Quoting fixes the splitting but not an empty value. A quoted empty variable redirects to a file with an empty name, which fails with No such file or directory, so check that the variable is set.
How does set -u change the error?
With set -u an unset variable fails earlier, with unbound variable, before bash tries the redirection. An empty but set variable still gives ambiguous redirect when unquoted.