0

I have written the following shell script.

if [ "$LOCALE" == "US" ]
then
   #do something

elif [ "$LOCALE" == "DE" || "$LOCALE" == "FR" || "$LOCALE" == "IT" || "$LOCALE" == "UK" ]
then
   #do something else.
fi

It is giving me the error [: missing ]. Can some one explain me what is wrong here?

2 Answers 2

2

Inside [...], use -a(and) and -o(or):

expr1 -a expr2

    True if both expr1 and expr2 are true.

expr1 -o expr2

    True if either expr1 or expr2 is true. 

Inside [[...]], you can use &&(and) and ||(or).

Sign up to request clarification or add additional context in comments.

Comments

1

The shell's if runs a command (or more technically, a "pipeline", as in this example):

if ls | grep xyzzy; then cmd1; cmd2; cmd3; fi

The exit-status of the command determines whether the then part gets run (or an else part if any). If the pipeline "succeeds", i.e., exits zero—as this one does if a file is at least partly named xyzzy—the "then" parts get run.

The [ command ... is a command! (It's built-in to all modern shell variants for speed, but it can be run as an ordinary command.)

sh-3.2$ [ a == b ]
sh-3.2$

The [ command reads arguments and demands that they end with ]:

sh-3.2$ [
sh: [: missing `]'
sh-3.2$

This is the error you're getting, because in sh syntax, prog1 || prog2 means to first run prog1, then only if it "fails" (exits non-zero), run prog2. So:

prog1 arg || grep xyzzy somefile

only runs the grep if prog1 fails. Hence, in turn:

[ x || grep xyzzy somefile

runs the grep if [ fails (which it does due to the syntax error).

(Aside: I often see people write things like:

ls | grep xyzzy
if [ $? = 0 ]; then cmd1; cmd2; cmd3; fi

This is unnecessarily long-winded, since $? is simply the exit status of the last pipeline, so this is just the same as my first example. Of course, some might say this stackoverflow answer is unnecessarily long-winded, too. :-) )

To get the [ program to test multiple conditions, you can use its -a ("and") and -o ("or") operators. Then all your testing will be done with a single run of the [ program:

if [ "$LOCALE" == DE -o "$LOCALE" == FR ]; then ...; fi

Or, use multiple invocations of the [ program, but this becomes more difficult here and starts to require parentheses or brackets:

([ "$LOCALE" == DE ] || [ "$LOCALE" == FR ]) && (cmd1; cmd2; ...)

{ [ "$LOCALE" == DE ] || [ "$LOCALE" == FR ]; } && { cmd1; cmd2; ...; }

if { [ "$LOCALE" == DE ] || [ "$LOCALE" == FR ]; } then cmd1; cmd2; ... fi

(I don't recommend these alternatives: they work, but are unnecessarily hard to read. If you must use one, the last is probably the least terrible.)

Consider replacing all of these with a case:

case "$LOCALE" in
DE|FR|IT|UK) cmd1; cmd2; cmd3;;
*) other commands here;;
esac

The case statement offers glob-style matching. For example, you can match user answers against mixed upper and lower case:

need_answer=true
while $need_answer; do
    echo -n "should I do a thing? (yes/no, default = no) "
    read answer
    case "$answer" in
    [yY]|[yY][eE]|[yY][eE][sS])
        # handle "yes" case here
        need_answer=false;;
    [nN]|[nN][oO]|"")
        # handle "no" case here
        need_answer=false;;
        ;;
    *) echo "please answer yes or no";;
    esac
done

The * works by glob-matching any input that was not matched earlier, since the various cases are taken linearly and the first match "wins".


Some shells (bash is particularly notable) have additional alternative syntaxes available for if statements.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.