为什么在空字符串上使用-d检查文件夹是否存在,返回true?


8

我在写一些脚本,写的东西像

ARTIFACTS="/SOME/PATH"
[ -d $ARTIFCATS ] && rm -rf $ARTIFACTS/*

发生的是,出于愚蠢,我执行了第二行而不执行第一行。原来[-d“”]返回true,表达式变为

rm -rf /*

幸运的是,这只是一台测试机,我不是sudo,但是尽管我丢失了一些数据

我的问题是,为什么[-d“”]返回true?文档明确指出它检查路径是否存在并且是文件夹

我通过使用解决了问题

[ -e $ARTIFACTS ]
这似乎有效

干杯


5
也许您执行了这两行。在上面的代码示例中,您永远不会设置ARTIFCATS。
2013年

2
我只是将其写为rm -rf $ARTIFACTS没有/*。这也将删除该$ARTIFACTS目录,这很好,因为如果我想确保在放入某些内容之前该目录已经存在,mkdir -p $ARTIFACTS无论如何我都会执行。它还会删除里面的隐藏文件$ARTIFACTS,这也很好,因为rm -rf $ARTIFACTS/*如果$ARTIFACTS包含任何我想保存的内容,我不会写。
ChristofferHammarström,2013年

@ChristofferHammarström非常正确
Moataz Elmasry

Answers:


9

1.这两个测试返回true

# [ -d ] && echo true || echo false
true
# [ -d $SOME_UNSET_VAR ] && echo true || echo false
true

根据POSIX(如@Tim所述)。

2.但这返回(如问题中所述不是 真的

# [ -d "" ] && echo true || echo false
false

因为test它使用两个参数调用(尽管第二个参数是一个空字符串)。

3.这就是为什么最好使用当前大多数shell提供[[ … ]]test[ … ])代替()的原因。此构造检查您是否提供了足够的参数(否则将引发错误并中止)

# [[ -d ]] && echo true || echo false
bash: unexpected argument `]]' to conditional unary operator
bash: syntax error near `]]'

或只是表现得像人期望的那样:

# [[ -d $SOME_UNSET_VAR ]] && echo true || echo false
false

4.而且,正如@Gilles指出的那样,更为重要的是双引号替换。因此-d "$SOME_UNSET_VAR",即使与(等于情况2)一样,扩展为-d ""并返回falsetest。因此,这也与Bourne shell兼容sh

# [ -d "$SOME_UNSET_VAR" ] && echo true || echo false
false

使用bash 3.00.16(1)和4.1.5(1)测试


1
Bash,ksh和zsh可以提供,[[ … ]]但不能简单提供sh。真正重要的做法是把周围的命令替换双引号[ -d "$ARTIFACTS" ]
吉尔(Gilles)'所以

6

在StackOverflow 上已经回答了这个问题。它说,根据POSIX标准,test如果仅使用一个非空参数(并且没有其他参数)调用它,则应始终返回成功。

这也应与情况test -e(事实上这是我的系统上),所以要小心。

而是使用:

[ -d "$ARTIFACTS" ]

test 即使变量为空,也将使用两个参数调用该方法,在这种情况下将返回false。


“恰好是一个非空的论点。” -这是一个令人误解的摘要-您所链接的页面提到的调用正好是一个参数,而该参数是非空的;对于非空参数后跟一个空参数的情况,则毫无意义。正确的解决方案应该是将变量名称括在引号中。
2013年

@ Random832谢谢!我实施了您建议的两项更改。
蒂姆(Tim)

[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]并没有更好的效果:它仅在$ARTIFACTS为空时满足特定情况,而在$ARTIFACTS可能包含空格或时失败\[?*[ -d "$ARTIFACTS" ]是执行此操作的正确方法(或[[ -d $ARTIFACTS ]]在具有的shell中[[ … ]])。
吉尔(Gilles)'所以

@吉尔斯你说得对,我删除了。谢谢!
蒂姆(Tim)

4

我通过使用似乎有效的[-e $ ARTIFACTS]解决了问题

错了。之所以有效,$ARTIFACTS是因为现在已设置为某些内容。

如果未设置变量,则说

[ -d $SOMEVAR ]

要么

[ -e $SOMEVAR ]

两个计算结果为true,因为它意味着说

[ -d ]

[ -e ]

分别。(说[ foobar ]总是会得出正确的结论。)

set -u

在这种情况下派上用场。 help set会告诉你:

  -u  Treat unset variables as an error when substituting.

[-e“”] && echo“ PRINT SOMETHING”不打印任何内容,那怎么可能是错误的呢?
Moataz Elmasry

2
ahh废话[-e $ ARTIFACTS]带有空的伪像,产生[-e]而不是[-e“”],明白了
Moataz Elmasry

4

看到您设置了变量ARTIFACTS,并且正在检查ARTIFCATS。可能是大惊小怪?

无论如何,-d和-e都会在未设置的变量上产生相同的结果。

因此,请使用双引号,它将为您提供帮助。

ARTIFACTS="/SOME/PATH"
[ -d "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS/"*

注意:如果您的“ / SOME / PATH”有任何带空格的文件夹,则您提到的脚本将因“期望二进制运算符”错误而中断。

例:

ARTIFACTS="/home backup/"

1) [ -d $ARTIFACTS ] && rm -rf $ARTIFACTS/*
bash: [: /home: binary operator expected

2) [ -d "$ARTIFACTS" ] && rm -rf "$ARTIFACTS"/*

会做的很好。不要忘记在rm调用中也加上引号(rm -rf $ARTIFACTS会很高兴地删除/home然后抱怨backup/*不存在)。

另外,包括-L检查将确保它是目录,而不仅仅是到目录的符号链接。

所以基本上

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.