在bash中运行以下命令:
stuff=`rpm -ql <some package> | grep dasdasdfd`
Run Code Online (Sandbox Code Playgroud)
(包中不存在的文件,退出代码= 1,标准输出为空)
if [ -f $stuff ]; then echo "whaaat"; fi
Run Code Online (Sandbox Code Playgroud)
上面的命令检查文件是否存在...但是:
file $stuff
Run Code Online (Sandbox Code Playgroud)
只需打印文件的使用信息......和
stat $stuff
Run Code Online (Sandbox Code Playgroud)
缺少操作数......
有人可以解释一下原因吗?这是一个错误吗?难道我做错了什么?我只是想确保包中的文件出现在fs上
你可能需要$stuff用引号括起来
if [ -f "$stuff" ]; then
Run Code Online (Sandbox Code Playgroud)
作为一般规则,您几乎总是希望在使用它们的任何地方添加路径名周围的引号.
我觉得将shell脚本中的变量或变量视为"宏"更有用,它们在首次使用时会扩展为它们的值.这与几乎所有其他编程语言中的变量不同.
因此,如果$stuff包含hello world(注意空格),它将与您键入的内容相同:
[ -f hello world ]
Run Code Online (Sandbox Code Playgroud)
这显然是一个错误.
在这种情况下,您提到您正在处理一个不存在的文件,因此$stuff实际上是空的,这就像键入:
[ -f ]
Run Code Online (Sandbox Code Playgroud)
这实际上是有效的,但总是成功的.这是一个有点模糊的test行为,从我们读到的POSIX规范,test如果只有一个参数(在这种情况下,参数是-f),总是成功:
1参数:
如果$ 1不为null,则退出true(0); 否则,退出false.
这可能是为了方便撰写:
[ $variable_that_may_or_may_not_be_defined ]
Run Code Online (Sandbox Code Playgroud)
如果添加引号,则传递2个参数,并且会发生更多理智:
if [ -f "" ]; then
Run Code Online (Sandbox Code Playgroud)
Martin Tournoij 的答案和DevSolar 的答案都提供了正确的解决方案和有用的背景信息:分别针对[ ... ]一种情况和[[ ... ]]另一种情况。
由于是否以及何时选择[[ ... ]]([ ... ]及其(虚拟)别名)可能并不明显test ...,让我尝试总结一下:
[ ... ](或test ...)。
[ ... ],因此您必须用双引号引用变量引用,除非您明确希望将所有 shell 扩展 - 特别是分词(通过空格自动拆分为多个标记)和通配符 - 应用于它们。[ -f "$stuff" ] # double-quoting required, if $stuff has embedded whitespacebash您可以使用它[[ ... ]]来获得更多功能并减少意外。
[[ ... ]]中进行解析,在该上下文中既不应用分词也不应用路径名扩展(通配符) (尽管确实会发生其他扩展,例如参数扩展),因此通常不需要双引号变量引用。[[ -f $stuff ]] # double-quoting optional请注意,ksh并且zsh还支持[[ ... ]](大概在行为上有细微的变化)。
有关更多背景信息,例如[[ ... ]]提供的附加功能,请继续阅读。
[[ ... ]]改进[ ... ]/test ...如下:
下面的“RHS”表示“右侧”,即二元运算符的右操作数。
(通常)不需要引用变量引用(除了在==和 的右侧=~指定文字字符串或子字符串)
f='some file'; [[ -f $f ]] # ok, double quotes optionalv='*'; [[ $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=~您希望将变量引用(或单引号文字)的值视为文字,则必须在右侧使用双引号引起来。
v='a.+'; [[ abc =~ ^"$v"$ ]] # does NOT matchshopt -s compat31将单引号和双引号字符串视为正则表达式。=~是特定于平台的,因此在一个平台上工作的正则表达式可能无法在另一个平台上工作(这是 bash 的行为依赖于平台的少数情况之一)。例如,在 Linux 上,您可以使用\band \</\>进行字边界断言,而 BSD/macOS 仅支持[[:<]]and [[:>]],而 Linux 又不支持 - 请参阅我的这个答案。(, ), 和!chars进行分组和否定。提供and(布尔 AND 和 OR)的使用&&||
[[ (3 -gt 2) && ! -f / ]] && echo yes[[ ... ]],&&的优先级高于||- ,与 OUTSIDE (所谓的 [command-]list 运算符,它们组合整个命令/命令列表)不同,它们具有相同的优先级。[和test都有 -a和-o,甚至 POSIX 规范也有关于test它们使用的警告)在 中[[ ... ]],您可以将条件分布在多行中以提高可读性,而无需使用行继续字符。( \),假设换行符位于或 之后,&&||如codeforester指出的那样。
[[ ... ]]比更快,[ ... ]尽管这通常并不重要。
[test实施说明:
[[a 是 shell关键字(在 、 和 中受支持bash)ksh,zsh它允许不同的解析规则,如上所述。[和test是所有主要的类 POSIX shell 中的内置函数bash( , ksh, zsh, dash)。[, 和都test作为外部实用程序(需要单独进程调用的可执行文件)存在。
[or ,例如将测试传递给or时。testfind -execxargs[实用程序可以想象为实用程序的符号链接来实现 test(只要知道test它是如何调用的,并]在调用时强制关闭[),但实际上它们通常(总是?)单独的可执行文件(在 Linux 和 macOS / BSD 上是这样) ,例如;在 Linux 上,它们的内容不同,而在 macOS / BSD 上,它们的内容是相同的(它们是同一文件的副本)。