/flags/bash/bash-n-syntax-check
bash -n script.sh: check a Bash script for syntax errors without running it
- tool
- Bash
- command
- bash
- flag
- -n (set -o noexec)
- default
- off: commands are read and executed
bash -n script.sh reads the whole script and reports syntax errors without executing anything. A valid script produces no output and exit status 0; a broken one prints the error with its line number and exits with status 2. It is the same as set -o noexec. It catches missing fi, done or esac, unbalanced quotes and parentheses, and stray tokens, the problems behind unexpected end of file and syntax error near unexpected token.
Broken and valid script
printf 'echo start\nif [ -f config ]; then\n echo found\n' > bad.sh
printf 'echo start\nfor f in *.txt; do echo "$f"; done\n' > ok.sh
printf 'echo "this would run"\nno_such_command --flag\n' > runtime.sh
bash -n bad.sh; echo "exit: $?"
bash -n ok.sh; echo "exit: $?"
bash -n runtime.sh; echo "exit: $?"
bash -n -c 'echo $((1 + )'; echo "exit: $?"Output
bad.sh: line 4: syntax error: unexpected end of file from `if' command on line 2 exit: 2 exit: 0 exit: 0 bash: -c: line 1: unexpected EOF while looking for matching `)' exit: 2
bad.sh is missing its fi, and Bash reports the end of the file together with the line where the unfinished if started. Neither ok.sh nor runtime.sh prints anything: nothing runs, so the echo is not executed, and the command that does not exist goes unnoticed, because -n only parses. -c code can be checked the same way.
What it checks
| Problem | Found by bash -n? |
|---|---|
| Missing fi, done, esac, } | yes |
| Unbalanced quotes, $( ), $(( )), [[ ]] | yes |
| Unexpected tokens: then without if, stray ) | yes |
| Misspelled or missing commands | no (run time: command not found) |
| Unset variables, wrong paths | no (run time; set -u helps) |
| Errors inside eval strings or sourced files | no: they are not parsed until executed |
| Portability to sh/dash | no: it checks Bash syntax only |
In CI and hooks
# check every script in the repository
find . -name '*.sh' -print0 | xargs -0 -n1 bash -n
# syntax plus common mistakes
bash -n deploy.sh && shellcheck deploy.shPair it with ShellCheck, which also reports quoting problems, unused variables and Bash-versus-sh issues. For debugging the behaviour of a script that parses fine, use set -x, and make it fail early on errors with set -euo pipefail.
Pitfalls
- Aliases are not expanded in non-interactive shells by default, so a script that relies on aliases can parse differently from your terminal.
- Here-documents with a misspelled terminator are reported only as end-of-file warnings (
here-document delimited by end-of-file), which do not change the exit status; read the output, not just$?. bash -non a file with Windows line endings can still pass, while running it fails with$'\r': command not found; convert withdos2unixorsed -i 's/\r$//'.- Run the check with the same Bash major version as production; newer syntax (for example
${var@U}in Bash 5.1) is an error on older versions.
FAQ
Does bash -n find misspelled commands or wrong variables?
No. It only parses the script. A command that does not exist, an unset variable or a wrong path is detected only when the script runs. ShellCheck finds many of these problems statically.
Can I use set -n inside a script?
Yes, set -n (set -o noexec) stops execution of the following commands while they are still parsed. Interactive shells ignore it, so you cannot lock yourself out of a terminal by typing it.
Why does bash -n pass but sh script.sh fails?
bash -n checks Bash syntax. If the script runs with sh (dash on Debian and Ubuntu), Bash-only syntax such as [[ ]], arrays or function name { } is a syntax error there. Check with the shell that will run the script, for example dash -n or sh -n.