关于bash:来自if [](方括号)的“[:太多参数”错误的含义

Meaning of “[: too many arguments” error from if [] (square brackets)

我找不到任何一个简单直接的资源来解释下面bash shell错误的含义并修复它,所以我在研究之后发布了我发现的内容。

错误:

1
-bash: [: too many arguments

谷歌友好版:bash open square bracket colon too many arguments

上下文:单个方括号中的if条件,带有简单的比较运算符,如equals、greater than等,例如:

1
2
3
4
VARIABLE=$(/some/command);
if [ $VARIABLE == 0 ]; then
  # some action
fi


如果您的$VARIABLE是一个包含空格或其他特殊字符的字符串,并且使用了单方括号(这是test命令的快捷方式),则该字符串可以拆分为多个单词。每一个都被视为一个独立的论点。

这样一个变量就可以分成多个参数:

1
2
3
4
5
6
7
VARIABLE=$(/some/command);  
# returns"hello world"

if [ $VARIABLE == 0 ]; then
  # fails as if you wrote:
  # if [ hello world == 0 ]
fi

同样的情况也适用于任何函数调用,该函数调用会生成包含空格或其他特殊字符的字符串。

易修复

将变量输出用双引号括起来,强制它保持为一个字符串(因此是一个参数)。例如,

1
2
3
4
VARIABLE=$(/some/command);
if ["$VARIABLE" == 0 ]; then
  # some action
fi

就这么简单。但是如果你也不能保证你的变量不是空字符串,或者一个只包含空白的字符串,那就跳到下面的"也要小心…"。

或者,另一种方法是使用双方括号(这是new test命令的快捷方式)。

但是,这只存在于bash(显然是korn和zsh)中,因此可能与/bin/sh等调用的默认shell不兼容。这意味着在某些系统上,例如,它可能在控制台上工作,但不在cron上工作,这取决于配置的方式。

如下所示:

1
2
3
4
VARIABLE=$(/some/command);
if [[ $VARIABLE == 0 ]]; then
  # some action
fi

同时也要注意[: unary operator expected错误

如果您看到"参数太多"错误,那么很可能是从输出不可预测的函数中得到了一个字符串。如果还可以得到一个空字符串(或所有空白字符串),即使使用上面的"快速修复",这也将被视为零参数,并且使用[: unary operator expected会失败。

如果您习惯了其他语言,那么它就是相同的"gotcha"——您不希望在对变量进行评估之前将变量的内容有效地打印到代码中。

这里有一个例子可以防止[: too many arguments[: unary operator expected错误:如果输出为空,则用默认值替换输出(在本例中为0),并用双引号将整个内容括起来:

1
2
3
4
VARIABLE=$(/some/command);
if ["${VARIABLE:-0}" == 0 ]; then
  # some action
fi

(此处,如果$variable为0或为空,则会发生此操作。当然,如果需要不同的行为,您应该将0(默认值)更改为不同的默认值)

最后一点:由于[test的快捷方式,上述所有情况对于test: too many argumentstest: unary operator expected的错误也是正确的。


刚刚碰到这篇文章,得到了相同的错误,试图测试两个变量是否都是空的(或非空的)。结果是一个复合比较-7.3。其他比较运算符-高级bash脚本指南;我认为我应该注意以下几点:

  • 我首先使用了-e->thinking it means"empty";但这意味着"file exists"-use -zfor testing empty variable(string)
  • 需要引用字符串变量
  • 对于复合逻辑和比较,可以是:
    • 使用两个test&&它们:[ ... ] && [ ... ]
    • 或者在单个test中使用-a运算符:[ ... -a ... ]

下面是一个工作命令(搜索目录中的所有txt文件,并转储grep找到的包含两个单词的文件):

1
2
3
4
5
6
find /usr/share/doc -name '*.txt' | while read file; do \
  a1=$(grep -H"description" $file); \
  a2=$(grep -H"changes" $file); \
  [ ! -z"$a1" -a ! -z"$a2"  ] && echo -e"$a1
 $a2"
; \
done

编辑2013年8月12日:相关问题注释:

注意,当用经典的test(单方括号[)检查字符串相等性时,必须在"is equal"运算符之间留出一个空格,在这种情况下,"is equal"运算符是一个单独的"equals"=符号(尽管两个相等的"signs"==似乎也被接受为相等运算符)。因此,这失败了(无声地):

1
2
3
4
5
6
7
8
$ if ["1"=="" ] ; then echo A; else echo B; fi
A
$ if ["1"="" ] ; then echo A; else echo B; fi
A
$ if ["1"="" ] && ["1"="1" ] ; then echo A; else echo B; fi
A
$ if ["1"=="" ] && ["1"=="1" ] ; then echo A; else echo B; fi
A

…但是增加空间-看起来都不错:

1
2
3
4
5
6
7
8
$ if ["1" ="" ] ; then echo A; else echo B; fi
B
$ if ["1" =="" ] ; then echo A; else echo B; fi
B
$ if ["1" ="" -a"1" ="1" ] ; then echo A; else echo B; fi
B
$ if ["1" =="" -a"1" =="1" ] ; then echo A; else echo B; fi
B


有时,如果你不小心碰了键盘,就去掉了一个空格。

1
2
3
if ["$myvar" ="something"]; then
    do something
fi

将触发此错误消息。注意"]"前的空格是必需的。


我的剧本也有同样的问题。但当我做了一些修改的时候,它对我有用。我是这样做的:

1
2
3
4
5
6
export k=$(date"+%k");
if [ $k -ge 16 ]
    then exit 0;
else
    echo"good job for nothing";
fi;

这样我就解决了我的问题。希望这对你也有帮助。