319

Is it possible to check a bash script syntax without executing it?

Using Perl, I can run perl -c 'script name'. Is there any equivalent command for bash scripts?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Tom Feiner
  • 20,656
  • 20
  • 48
  • 51
  • 1
    Related: [Is there a static analysis tool like Lint or Perl::Critic for shell scripts?](http://stackoverflow.com/q/3668665/320399) – blong Jun 03 '14 at 20:47

10 Answers10

452
bash -n scriptname

Perhaps an obvious caveat: this validates syntax but won't check if your bash script tries to execute a command that isn't in your path, like ech hello instead of echo hello.

Chris
  • 44,602
  • 16
  • 137
  • 156
andy
  • 6,888
  • 1
  • 19
  • 17
  • 9
    In bash's manpage, under "SHELL BUILTIN COMMANDS / set", -n is documented, and as the beginning of the manpage states, bash interprets all single-character options that `set` does. – ephemient Oct 05 '08 at 20:55
  • 25
    to add to the (for me) non obvious caveat, it also won't catch an error caused by a missing space `if ["$var" == "string" ]` instead of `if [ "$var" == "string" ]` – Brynjar Aug 05 '11 at 16:13
  • 15
    @Brynjar That's because it's just syntax checking. The open bracket isn't syntax, that's the name of the function to run. `type [` says "[ is a shell builtin". It ultimately delegates to the `test` program, but it expects a closing bracket, as well. So it's like `if test"$var"`, which isn't what the author meant, but is syntactically valid (say $var has a value of "a", then we will see "bash: testa: command not found"). Point is that syntactically, there is no missing space. – Joshua Cheek Jul 20 '14 at 04:30
  • 2
    @JoshuaCheek: Builtin `[` is only invoked in this case if `$var` happens to expand to an _empty string_. If `$var` expands to a _non-empty_ string, `[` is _concatenated_ with that string and is interpreted as a _command_ name (not _function_ name) by Bash, and, yes, that is _syntactically_ valid, but, as you state, obviously not the intent. If you use `[[` instead of `[`, even though `[[` is a shell _keyword_ (rather than a builtin), you'll get the same result, because the unintended string concatenation still overrides recognition of the keyword. – mklement0 Jun 11 '15 at 21:55
  • 3
    @JoshuaCheek: Bash is still _syntax_-checking here: it's checking _simple-command invocation_ syntax: `["$var"` is _syntactically_ a valid _command-name_ expression; similarly, tokens `==` and `"$string"` are valid command _arguments_. (Generally, builtin `[` is parsed with _command_ syntax, whereas `[[` - as a shell _keyword_ - is parsed differently.) The shell builtin `[` does _not_ _delegate_ to the "`test` program" (external utility): `bash`, `dash`, `ksh`, `zsh` all have _builtin_ versions of both `[` and `test`, and they do _not_ call their external-utility counterparts. – mklement0 Jun 11 '15 at 23:02
  • 2
    The bottom line is: using just Bash itself, `-n` is your best bet, but that won't catch many errors beginners are likely to make; to catch these, use http://shellcheck.net or its CLI, as detailed in [dvd818's answer](http://stackoverflow.com/a/19573083/45375). – mklement0 Jun 12 '15 at 02:03
  • Another caveat: won't take in account options set in script. Eg if you add `shopt -s extglob` and then do `foo@(bar|baz))` in `case`, `bash -n` will see that as syntax error (`bash -s extglob -n` will not, but you never know which part of the script is affected) – Alois Mahdal Jul 02 '15 at 14:21
  • @AloisMahdal Unfortunately that would be impossible to fix in the general case, since shell options can change mid-script or even based on information that can only be known at runtime. – Daniel H May 12 '18 at 07:40
  • Here's the documentation from `man bash`: `-n Read commands but do not execute them. This may be used to check a shell script for syntax errors. This is ignored by interactive shells.` – wisbucky Mar 25 '19 at 18:23
  • is the -n option version or os specific; doesnt seem to work on osx – qodeninja Sep 12 '19 at 02:57
159

Time changes everything. Here is a web site which provide online syntax checking for shell script.

I found it is very powerful detecting common errors.

enter image description here

About ShellCheck

ShellCheck is a static analysis and linting tool for sh/bash scripts. It's mainly focused on handling typical beginner and intermediate level syntax errors and pitfalls where the shell just gives a cryptic error message or strange behavior, but it also reports on a few more advanced issues where corner cases can cause delayed failures.

Haskell source code is available on GitHub!

dvd818
  • 1,727
  • 1
  • 10
  • 10
  • 7
    Great tip; on OSX you can now also install the shellcheck.net CLI, `shellcheck`, via [Homebrew](http://brew.sh): `brew install shellcheck`. – mklement0 Jun 12 '15 at 02:20
  • 5
    Also on debian&friends: `apt-get install shellcheck` – that other guy Jul 31 '15 at 01:36
  • For Ubuntu trusty this package must be installed from `trusty-backports`. – Peterino Aug 04 '15 at 21:19
  • As mentioned above, trusty dependency needed, and can install it as below in ubuntu 14.04: sudo apt-get -f install, then: sudo sudo apt-get install shellcheck – zhihong Dec 08 '16 at 15:32
  • 1
    This is really useful, but it does not use Bash’s parser but its own. In most cases this is good enough and it can identify both parsing and other issues, but there is at least one edge case (and probably others I haven’t seen) where it doesn’t parse quite the same way. – Daniel H May 12 '18 at 07:37
  • If you want to run your program only if it passes shellcheck: https://gitlab.com/es20490446e/shellproof – Alberto Salvia Novella Feb 13 '21 at 01:41
42

I also enable the 'u' option on every bash script I write in order to do some extra checking:

set -u 

This will report the usage of uninitialized variables, like in the following script 'check_init.sh'

#!/bin/sh
set -u
message=hello
echo $mesage

Running the script :

$ check_init.sh

Will report the following :

./check_init.sh[4]: mesage: Parameter not set.

Very useful to catch typos

Diego Tercero
  • 1,143
  • 1
  • 11
  • 17
  • 5
    i set these flags always in my bash scripts, if it passes these, it's good to go "set -o errexit" "set -o nounset" "set -o pipefail" – μολὼν.λαβέ Jan 10 '16 at 06:30
  • 3
    +1 for `set -u` although this does not really answer the question, because you have to run the script to get the error message. Not even `bash -n check_init.sh` shows that warning – rubo77 Jul 06 '17 at 09:30
26
sh  -n   script-name 

Run this. If there are any syntax errors in the script, then it returns the same error message. If there are no errors, then it comes out without giving any message. You can check immediately by using echo $?, which will return 0 confirming successful without any mistake.

It worked for me well. I ran on Linux OS, Bash Shell.

Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
Jeevan
  • 269
  • 3
  • 2
  • 1
    Though not exactly related to bash syntax check - using set -x and set +x for debugging the full script or sections of the script is quite useful – GuruM Aug 01 '12 at 12:47
  • Thanks for this didn't know didn't know about the -n but what I wanted was @GuruM > sh -x test.sh as that displays the generated output of the script – zzapper Jun 19 '13 at 10:24
  • 1
    Yes. I forgot to mention that you can do the following At command line: 1) bash -x test.sh #this runs the whole script in 'debug mode' 2) set +x; bash test.sh; set -x #set debug mode on/off before/after script run In the script: a) #!/bin/bash -x #add 'debug mode' at top of script b) set +x; code; set -x #add 'debug mode' for any section of script – GuruM Jun 19 '13 at 13:23
  • `sh -n` will probably not check that the script is valid Bash script. It might give false negatives. `sh` is some Bourne shell variant that is usually not Bash. For example in Ubuntu Linux `realpath -e $(command -v sh)` gives /bin/dash – jarno Nov 04 '19 at 15:49
6

I actually check all bash scripts in current dir for syntax errors WITHOUT running them using find tool:

Example:

find . -name '*.sh' -print0 | xargs -0 -P"$(nproc)" -I{} bash -n "{}"

If you want to use it for a single file, just edit the wildcard with the name of the file.

Bensuperpc
  • 1,275
  • 1
  • 14
  • 21
Gerald Hughes
  • 5,771
  • 20
  • 73
  • 131
4

null command [colon] also useful when debugging to see variable's value

set -x
for i in {1..10}; do
    let i=i+1
    : i=$i
done
set - 
mug896
  • 1,777
  • 1
  • 19
  • 17
3

For only validating syntax:

shellcheck [programPath]

For running the program only if syntax passes, so debugging both syntax and execution:

shellproof [programPath]

3

Bash shell scripts will run a syntax check if you enable syntax checking with

set -o noexec

if you want to turn off syntax checking

set +o noexec
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Mike
  • 31
  • 1
2

There is BashSupport plugin for IntelliJ IDEA which checks the syntax.

Cengiz
  • 5,375
  • 6
  • 52
  • 77
  • 1
    But it won't work if your files don't end with `.sh` or other extension associated with Bash scripts, which is the case if you generate the scripts using some templating tool like ERB (then they end with `.erb`). Please vote for https://youtrack.jetbrains.com/issue/IDEA-79574 if you want it fixed! – Greg Dubicki Sep 22 '16 at 08:40
1

If you need in a variable the validity of all the files in a directory (git pre-commit hook, build lint script), you can catch the stderr output of the "sh -n" or "bash -n" commands (see other answers) in a variable, and have a "if/else" based on that

bashErrLines=$(find bin/ -type f -name '*.sh' -exec sh -n {} \;  2>&1 > /dev/null)
  if [ "$bashErrLines" != "" ]; then 
   # at least one sh file in the bin dir has a syntax error
   echo $bashErrLines; 
   exit; 
  fi

Change "sh" with "bash" depending on your needs

E Ciotti
  • 4,740
  • 1
  • 25
  • 17