1

I was looking at an answer in another thread about which bracket pair to use with if in a bash script. [[ is less surprising and has more features such as pattern matching (=~) whereas [ and test are built-in and POSIX compliant making them portable.

Recently, I was attempting to test the result of a grep command and it was failing with [: too many arguments. I was using [. But, when I switched to [[ it worked. How would I do such a test with [ in order to maintain the portability?

This is the test that failed:

#!/bin/bash

cat > slew_pattern << EOF
g -x"$
EOF


if [ $(grep -E -f slew_pattern /etc/sysconfig/ntpd) ]; then
        echo "slew mode"

else
        echo "not slew mode"

fi

And the test that succeeded:

#!/bin/bash

cat > slew_pattern << EOF
g -x"$
EOF

if [[ $(grep -E -f slew_pattern /etc/sysconfig/ntpd) ]]; then
        echo "slew mode"

else
        echo "not slew mode"

fi
Community
  • 1
  • 1
theillien
  • 1,189
  • 3
  • 19
  • 33
  • Related, but not really a good duplicate: http://stackoverflow.com/questions/10586213/encounter-unary-operator-expected-in-bash-script – tripleee Jun 25 '15 at 05:25
  • 1
    If you are looking to set `ntp` in `slew mode` to compensate for the leapsecond before 6/30 and restore the original config on 7/1, take a look at [**Fix leapsecond 2015 set ntp slew mode**](http://pastebin.com/7cqTT5KD) **Note:** the restart command is distribution dependent, so you may need to modify it. – David C. Rankin Jun 25 '15 at 05:50

1 Answers1

4
if [ $(grep -E -f slew_pattern /etc/sysconfig/ntpd) ]; then

This command will certainly fail for multiple matches. It will throw an error as the grep output is being split on line ending.

Multiple matches of grep are separated by new line and the test command becomes like:

[ match1 match2 match3 ... ]

which doesn't make much of a sense. You will get different error messages as the number of matches returned by grep (i.e the number of arguments for test command [).

For example:

2 matches will give you unary operator expected error

3 matches will give you binary operator expected error and

more than 3 matches will give you too many arguments error or such, in Bash.


You need to quote variables inside [ to prevent word splitting.

On the other hand, the Bash specific [[ prevents word splitting by default. Thus the grep output doesn't get split on new line and remains a single string which is a valid argument for the test command.

So the solution is to look only at the exit status of grep:

if  grep -E -f slew_pattern /etc/sysconfig/ntpd; then

Or use quote when capturing output:

if [ "$(grep -E -f slew_pattern /etc/sysconfig/ntpd)" ]; then

Note:

  1. You don't really need to capture the output here, simply looking at the exit status will suffice.
  2. Additionally, you can suppress output of grep command to be printed with -q option and errors with -s option.
Jahid
  • 21,542
  • 10
  • 90
  • 108