Skip to content

/flags/bash/bash-n-syntax-check

bash -n script.sh: check a Bash script for syntax errors without running it

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
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

bash
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

ProblemFound by bash -n?
Missing fi, done, esac, }yes
Unbalanced quotes, $( ), $(( )), [[ ]]yes
Unexpected tokens: then without if, stray )yes
Misspelled or missing commandsno (run time: command not found)
Unset variables, wrong pathsno (run time; set -u helps)
Errors inside eval strings or sourced filesno: they are not parsed until executed
Portability to sh/dashno: it checks Bash syntax only

In CI and hooks

bash
# 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.sh

Pair 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 -n on a file with Windows line endings can still pass, while running it fails with $'\r': command not found; convert with dos2unix or sed -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.

Sources