Wrapping the whole into a function seems to do the trick:
#!/bin/bash -e
main () {
readonly a=(1 2)
# A syntax error is here:
if (( "${a[#]}" == 2 )); then
echo ok
else
echo not ok
fi
echo status $?
echo 'Bad: has not aborted execution on syntax error!'
}
main "$@"
Result:
$ ./sh-on-syntax-err
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$
Though I have no clue why - maybe someone else can explain?
Keywords like if
, then
, else
, fi
, for
, case
and so on need to be in a place where the shell expects a command name. Otherwise they are treated as ordinary words. For example,
echo if
just prints if
, it doesn't start a conditional instruction.
Thus, in the line
if [ -r "$NAME" -af "$NAME" ] then
the word then
is an argument of the command [
(which it would complain about if it ever got to run). The shell keeps looking for the then
, and finds a fi
in command position. Since there's an if
that's still looking for its then
, the fi
is unexpected, there's a syntax error.
You need to put a command terminator before then
so that it's recognized as a keyword. The most common command terminator is a line break, but before then
, it's common to use a semicolon (which has exactly the same meaning as a line break).
if [ -r "$NAME" -af "$NAME" ]; then
or
if [ -r "$NAME" -af "$NAME" ]
then
Once you fix that you'll get another error from the command [
because it doesn't understand -af
. You presumably meant
if [ -r "$NAME" -a -f "$NAME" ]; then
Although the test commands look like options, you can't bundle them like this. They're operators of the [
command and they need to each be a separate word (as do [
and ]
).
By the way, although [ -r "$NAME" -a -f "$NAME" ]
works, I recommend writing either
[ -r "$NAME" ] && [ -f "$NAME" ]
or
[[ -r $NAME && -f $NAME ]]
It's best to keep [ … ]
conditionals simple because the [
command can't distinguish operators from operand easily. If $NAME
looks like an operator and appears in a position where the operator is valid, it could be parsed as an operator. This won't happen in the simple cases seen in this answer, but more complex cases can be risky. Writing this with separate calls to [
and using the shell's logical operators avoids this problem.
The second syntax uses the [[ … ]]
conditional construct which exists in bash (and ksh and zsh, but not plain sh). This construct is special syntax, whereas [
is parsed like any other command, thus you can use things like &&
inside and you don't need to quote variables except in arguments to some string operators (=
, ==
, !=
, =~
) (see When is double-quoting necessary? for details).
Best Answer
You have mismatched
[[
with]
.[[
should always be closed with]]
and[
with]
. Use:Better yet, since you are using
[[
anyway:The other mistake, which I didn't notice until formatting was applied, is that you're doing:
You should be doing:
With the first form,
$a
and$b
are replaced by the shell with their contents, so if you hadn't set them before this line, the final command would be:(in which case the value read would be stored in the
REPLY
variable.) And if you had seta
to something (likea="blah blah"
), it would look like: