Bash – Easiest way to find missing quote in a bash a script

bashdebuggingshell-script

I have a bash script which is currently 700+ lines long. After a particularly long round of editing, it now errors like so:

./run_me.sh: line 693: unexpected EOF while looking for matching `''
./run_me.sh: line 702: syntax error: unexpected end of file

However, there is nothing wrong I can see with line 693. There isn't even a quote on it.

I've tried running bash -x run_me.sh, and can see the last line run, but there is nothing wrong with that line either (and the code between that line and 693 is, effectively, most of the code).

I can comment out whole swathes of the code, but then I am more likely to see errors due to missing functions, etc, rather than the EOF error I am getting.

So how is someone supposed to find the missing quote, if the line number is reported incorrectly?

(Why is bash's line number reporting so far off, anyway?)

Edit

Incidentally, I found my particular error (by commenting swathes), which was actually a missing } in a variable expansion at an arbitrary line number nowhere near the line indicated by the error message — quote-based syntax highlighting did not help here, e.g. with the missing brace in "${MY_ARRAY[@]" (should be "${MY_ARRAY[@]}").

Best Answer

A low-tech approach is

tr -cd "'\n" < run_me.sh | awk 'length%2==1 {print NR, $0}'

The tr deletes all characters except for single quotes and newlines, and the awk identifies the lines that have an odd numbers of characters (i.e., non-matching quotes).  Check them out individually; note that valid strings like "That's not a bug!" will be flagged.

If this doesn’t highlight the problem, try again with tr -cd '"\n' (to find non-matching double quotes).  Next, expand the tr to allow (), [], and ultimately {} to go through.  At this point, manual inspection of the output becomes a less fruitful pursuit; while ( and ) are usually matched within the same line, and quotes and square brackets almost always are, this is not true of { and }.  (However, looking for non-matching { and } would find the problem that the OP actually had, namely, typing "${MY_ARRAY[@]" instead of "${MY_ARRAY[@]}").

If you have narrowed the problem down to a curly brace mismatch, and especially if you have narrowed down a range in the file, go to Plan B.  Open the file in vim (or vi).  Go to a { character that you believe is before the error and type %.  If the cursor jumps to the } that you think matches the { you started on, move forward to the next { and repeat.  If it jumps to a } that doesn’t match the { you started on, the error occurs between those two braces.  If it doesn’t move, the error occurs between your current location and the end of the file.  Zoom in.

This approach may be slightly better suited to program files in languages like C, C++, and Java (if their compilers don’t do a good enough job), but it should work fairly well for shell scripts.

Related Question