对字符串中的特殊字符采取措施后,会出现BASH错误“[: too many arguments”的原因是什么?
What would cause the BASH error "[: too many arguments" after taking measures for special characters in strings?
我正在编写一个简单的脚本来检查一些存储库更新,如果需要,我正在从这些更新中制作新的软件包以安装它引用的那些程序的新版本(在 Arch Linux)。所以我在执行真正的脚本之前做了一些测试。
问题是我从这段代码中收到错误 [: excessive number of arguments
(但我认为正确的翻译应该是 [: too many arguments
) :
# Won't work despite the double quoted $r
if [ "$r" == *"irt"* ]; then
echo "TEST"
fi
代码已通过添加双方括号修复,感谢 this SO answer made by @user568458:
# Makes the code works
if [[ "$r" == *"irt"* ]]; then
echo "TEST"
fi
请注意 $r
定义为:
# Double quotes should fix it, right? Those special characters/multi-lines
r="$(ls)"
另请注意,所有内容都在一个循环中,并且循环成功进行。每次 if 比较匹配时都会出现问题,不打印发出的 "TEST"
,直接跳到循环的下一次迭代(没问题:此 if
之后不存在代码)。
我的问题是:为什么每次匹配字符串都会报错?据我了解,双引号足以修复它。另外,如果我指望双方括号来修复它,一些shell将无法识别它(指上面提到的answer)。还有什么选择?
Shell 脚本似乎是一种全新的编程范式。我从来没有完全掌握细节,也未能获得很好的资源。
由于您正在检查具有特定文件名的文件,我建议明确使用 find
。像
r="$(find . -name '*irt*' 2> /dev/null)"
if [ ! -z "$r" ]; then
echo "found: $r"
fi
单括号是 shell 内置的, 而双括号是 shell 关键字。 不同之处在于 builtin 的行为类似于命令:分词、文件模式匹配等发生在 shell 解析命令时。如果您有匹配模式 *irt*
的文件,比如 file1irt.txt
和 file2irt.txt
,那么当 shell 解析命令时
[ "$r" = *irt* ]
它展开 $r
,匹配所有匹配模式 *irt*
的文件,最终看到命令:
[ expansion_of_r = file1irt.txt file2irt.txt ]
这会产生错误。没有引号可以解决这个问题。事实上,单括号形式根本无法处理模式匹配。
另一方面,双括号不像命令那样处理; Bash不会进行任何分词和文件模式匹配,所以它真的看到了
[[ "expansion_of_r" = *irt* ]]
在这种情况下,右侧是一个模式,因此 Bash 测试左侧是否匹配该模式。
对于便携式替代方案,您可以使用:
case "$r" in
(*irt*) echo "TEST" ;;
esac
但是现在这里有一个可怕的反模式。你在做:
r=$(ls)
if [[ "$r" = *irt* ]]; then
echo "TEST"
fi
我的理解是你想知道当前目录下是否有匹配模式*irt*
的文件。可移植的可能性是:
for f in *irt*; do
if [ -e "$f" ]; then
echo "TEST"
break
fi
done
我正在编写一个简单的脚本来检查一些存储库更新,如果需要,我正在从这些更新中制作新的软件包以安装它引用的那些程序的新版本(在 Arch Linux)。所以我在执行真正的脚本之前做了一些测试。
问题是我从这段代码中收到错误 [: excessive number of arguments
(但我认为正确的翻译应该是 [: too many arguments
) :
# Won't work despite the double quoted $r
if [ "$r" == *"irt"* ]; then
echo "TEST"
fi
代码已通过添加双方括号修复,感谢 this SO answer made by @user568458:
# Makes the code works
if [[ "$r" == *"irt"* ]]; then
echo "TEST"
fi
请注意 $r
定义为:
# Double quotes should fix it, right? Those special characters/multi-lines
r="$(ls)"
另请注意,所有内容都在一个循环中,并且循环成功进行。每次 if 比较匹配时都会出现问题,不打印发出的 "TEST"
,直接跳到循环的下一次迭代(没问题:此 if
之后不存在代码)。
我的问题是:为什么每次匹配字符串都会报错?据我了解,双引号足以修复它。另外,如果我指望双方括号来修复它,一些shell将无法识别它(指上面提到的answer)。还有什么选择?
Shell 脚本似乎是一种全新的编程范式。我从来没有完全掌握细节,也未能获得很好的资源。
由于您正在检查具有特定文件名的文件,我建议明确使用 find
。像
r="$(find . -name '*irt*' 2> /dev/null)"
if [ ! -z "$r" ]; then
echo "found: $r"
fi
单括号是 shell 内置的, 而双括号是 shell 关键字。 不同之处在于 builtin 的行为类似于命令:分词、文件模式匹配等发生在 shell 解析命令时。如果您有匹配模式 *irt*
的文件,比如 file1irt.txt
和 file2irt.txt
,那么当 shell 解析命令时
[ "$r" = *irt* ]
它展开 $r
,匹配所有匹配模式 *irt*
的文件,最终看到命令:
[ expansion_of_r = file1irt.txt file2irt.txt ]
这会产生错误。没有引号可以解决这个问题。事实上,单括号形式根本无法处理模式匹配。
另一方面,双括号不像命令那样处理; Bash不会进行任何分词和文件模式匹配,所以它真的看到了
[[ "expansion_of_r" = *irt* ]]
在这种情况下,右侧是一个模式,因此 Bash 测试左侧是否匹配该模式。
对于便携式替代方案,您可以使用:
case "$r" in
(*irt*) echo "TEST" ;;
esac
但是现在这里有一个可怕的反模式。你在做:
r=$(ls)
if [[ "$r" = *irt* ]]; then
echo "TEST"
fi
我的理解是你想知道当前目录下是否有匹配模式*irt*
的文件。可移植的可能性是:
for f in *irt*; do
if [ -e "$f" ]; then
echo "TEST"
break
fi
done