在bash中运行以下命令:
stuff=`rpm -ql <some package> | grep dasdasdfd`
(包中不存在的文件,退出代码= 1,标准输出为空)
if [ -f $stuff ]; then echo "whaaat"; fi
上面的命令检查文件是否存在......但是:
file $stuff
只需打印文件的使用信息...和
stat $stuff
缺少操作数......
有人可以解释一下原因吗?这是一个错误吗?难道我做错了什么?我只是想确保包中的文件出现在fs
上答案 0 :(得分:6)
您可能需要在引号中加注$stuff
if [ -f "$stuff" ]; then
作为一般规则,您几乎始终希望在您使用它们的任何地方的路径名周围添加引号。
我觉得在shell脚本中考虑或变量更有用的是&#34;宏&#34;,它们在首次使用时会扩展为它们的值。这与几乎所有其他编程语言中的变量不同。
因此,如果$stuff
包含hello world
(请注意空格),则与您输入的内容相同:
[ -f hello world ]
这显然是一个错误。
在这种情况下,您提到您正在处理不存在的文件,因此$stuff
实际上是空的,这就像打字:
[ -f ]
哪个实际上有效,但总是成功。这是一个模糊的test
行为,从POSIX spec我们读到test
总是成功,如果只有一个参数(在这种情况下,参数是-f
):
1个论点:
如果$ 1不为null,则退出true(0);否则,退出false。
这可能是为了方便撰写:
[ $variable_that_may_or_may_not_be_defined ]
如果你添加引号,你就会传递2个参数,并且会发生更加明智的事情:
if [ -f "" ]; then
答案 1 :(得分:3)
Carpetsmoker说,其中一个选择就是将$stuff
放在引号中。
但是因为这是标记为bash,并且因为在文件名中考虑空白是一种痛苦,所以你可以选择:
if [[ -f $stuff ]]
与[
的别名test
相对,[[
构造“知道”如何正确处理$stuff
的内容。
答案 2 :(得分:3)
Martin Tournoij's answer和DevSolar's answer都提供了正确的解决方案和有用的背景信息:在一种情况下为[ ... ]
,在另一种情况下为[[ ... ]]
。
由于选择[[ ... ]]
超过[ ... ]
(及其(虚拟)别名test ...
)可能不明显,让我试一试摘要:
[ ... ]
(或test ...
)。
[ ... ]
内的标记解析就像传递给可执行文件的参数一样,所以必须双引号变量引用,除非您明确要求所有shell扩展 - 特别是单词拆分(通过空格自动拆分成多个令牌)和通配 - 应用于它们。[ -f "$stuff" ] # double-quoting required, if $stuff has embedded whitespace
bash
运行,则可以使用[[ ... ]]
获取更多功能,减少意外情况即可。
[[ ... ]]
内的标记在特殊上下文中解析,其中既不应用分词也不应用路径名扩展(globbing)(尽管其他扩展,例如参数扩展,做发生),因此通常不需要双引号变量引用。[[ -f $stuff ]] # double-quoting optional
请注意,ksh
和zsh
也支持[[ ... ]]
(可能是行为的微妙变化)。
有关更多背景信息,例如[[ ... ]]
提供的其他功能,请继续阅读。
[[ ... ]]
改进[ ... ]
/ test ...
,如下所示:
&#34; RHS&#34;下面表示&#34;右侧&#34;,即二元运算符的右操作数。
(通常)不需要引用变量引用(==
和=~
的RHS除外,以指定文字 string或substring(s))
f='some file'; [[ -f $f ]] # ok, double quotes optional
v='*'; [[ $v == '*' ]] # ok, double quotes optional
[[ ... ]]
内部既不应用单词拆分也不应用路径名扩展,因此可以安全地使用对其值已嵌入空格和/或值通常为*
的变量的非加引号引用导致全球化。提供字符串模式匹配与=
/ ==
,其中未加引号的模式RHS(或至少是未引用的模式元素)。
[[ abc == a* ]] && echo yes # matches; use of = instead of == works too
=
/ ==
的RHS上,您必须双引号变量引用(或单引号文字)如果您希望将其值视为文字。
v='a*'; [[ abc == "$v" ]] # does NOT match
=~
匹配的正则表达式,在RHS上使用未加引号扩展正则表达式(或至少不带引号的正则表达式元数据)。
[[ abc =~ ^a.+$ ]] && echo yes # matches
=~
的RHS中,如果您希望其值为,则必须双引号变量引用(或单引号文字)被视为文字。
v='a.+'; [[ abc =~ ^"$v"$ ]] # does NOT match
shopt -s compat31
将单引号和双引号字符串视为正则表达式。=~
理解的正则表达式方言是特定于平台的,因此在一个平台上运行的正则表达式可能不适用于另一个平台(这是bash行为依赖于平台的少数情况之一。例如,在Linux上,您可以使用\b
和\<
/ \>
进行字边界断言,而BSD / macOS仅支持[[:<]]
和[[:>]]
,反过来,Linux并不支持 - 请参阅我的this answer。(
,)
和!
字符提供分组和否定。提供使用&&
和||
(布尔AND和OR)
[[ (3 -gt 2) && ! -f / ]] && echo yes
[[ ... ]]
内,&&
的更高优先级高于||
- 与OUTSIDE不同(如所谓的[command-]列表运算符,其中它们组合了整个命令/命令列表),它们具有相等的优先级。[
和test
有-a
和-o
,even the POSIX spec. for test
cautions against their use)在[[ ... ]]
内,您可以在多行中传播条件以提高可读性,而无需使用行继续符号。 (\
),假设换行符在&&
或||
之后,codeforester指出。
[[ ... ]]
更快 比[ ... ]
更快,但这通常无关紧要。
实施说明重新[
和test
:
[[
a是shell 关键字(bash
,ksh
和zsh
支持),它允许不同的解析规则,如如上所述。[
和test
在所有类似POSIX的主要shell中都是 builtins (bash
,ksh
,{{1} },zsh
)。 dash
和[
都作为外部实用程序(可执行文件存在,需要单独的进程调用),mandated by POSIX。
test
或[
调用方案,例如将测试传递给test
或find -exec
。xargs
实用程序可以实现为[
实用程序的符号链接(只要test
知道如何调用它,并强制关闭test
时调用为]
),实际上它们通常(总是?)单独的可执行文件(例如,在Linux和macOS / BSD上都是如此;在Linux上,它们的内容不同,而在macOS / BSD上它们的内容是相同的(它们是是同一文件的副本))。