题 检查shell脚本中是否存在目录


在shell脚本中,可以使用什么命令来检查目录是否存在?


2999
2017-09-12 20:06


起源




答案:


要检查shell脚本中是否存在目录,可以使用以下命令:

if [ -d "$DIRECTORY" ]; then
  # Control will enter here if $DIRECTORY exists.
fi

或者检查目录是否不存在:

if [ ! -d "$DIRECTORY" ]; then
  # Control will enter here if $DIRECTORY doesn't exist.
fi

但是,作为 Jon Ericson 指出,如果您没有考虑到目录的符号链接也会通过此检查,则后续命令可能无法按预期工作。 例如。运行这个:

ln -s "$ACTUAL_DIR" "$SYMLINK"
if [ -d "$SYMLINK" ]; then 
  rmdir "$SYMLINK" 
fi

会产生错误信息:

rmdir: failed to remove `symlink': Not a directory

因此,如果后续命令需要目录,则可能必须区别对待符号链接:

if [ -d "$LINK_OR_DIR" ]; then 
  if [ -L "$LINK_OR_DIR" ]; then
    # It is a symlink!
    # Symbolic link specific commands go here.
    rm "$LINK_OR_DIR"
  else
    # It's a directory!
    # Directory command goes here.
    rmdir "$LINK_OR_DIR"
  fi
fi

请特别注意用于包装变量的双引号,其原因由8jean解释 在另一个答案

如果变量包含空格或其他异常字符,则可能导致脚本失败。


4144
2017-09-12 20:07



如果你想用GNU工具安全地使用它,请使用 -- 强烈推荐(选项结束标记)。否则,如果您的变量包含看起来像选项的内容,则脚本将像空格一样失败。 - Marc Mutz - mmutz
对于现代版本的bash,ksh等[...]是内置的 - fpmurphy1
要记住一件事: [ ! -d "$DIRECTORY" ] 如果是,那将是真的 $DIRECTORY 不存在,或者如果 不 存在但不是目录。考虑类似的事情 if [ ! -d "$DIRECTORY" ] ; then mkdir "$DIRECTORY" ; fi;如果这会失败 "$DIRECTORY" 是一个文件。 (当然你应该检查一下 mkdir 无论如何都成功了;它有很多原因可能会失败。) - Keith Thompson
值得一提的是,一旦执行了检查,由于其他流程,情况可能已经发生变化。在许多情况下,最好只创建或使用目录并对失败做出反应。 - Alfe
而不是测试目录(-d)和符号链接(-L),只是在变量上附加一个斜杠就更容易了 if [ -d "${THING:+$THING/}" ]。目录不会介意额外的斜杠。文件将评估为false。空将保持为空,所以是假的。符号链接将被解析到其目的地。当然,这取决于你的目标。如果你想 走 在那里,这很好。如果你想 删除它,那么这个答案中的代码更好。 - ghoti


请记住在引用变量时始终将变量用双引号括起来 一个bash脚本。这些天孩子们长大了,他们的目录名称中可以有空格和许多其他有趣的字符。 (太空!回到我的日子,我们没有任何奇特的空间!;))

有一天,其中一个孩子将运行你的脚本 $DIRECTORY 设置 "My M0viez" 而且你的剧本会爆炸。你不希望这样。所以使用它。

if [ -d "$DIRECTORY" ]; then
    # Will enter here if $DIRECTORY exists, even if it contains spaces
fi

454
2017-09-15 22:00



使用双引号的另一个原因是因为某些原因未设置$ DIRECTORY。 - Jon Ericson♦
“总是用双引号将变量包装在一个bash脚本中。”对于bash,使用[[...]]时技术上不必要;看到 tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS (注意:没有单词拆分):“[[和]]之间没有文件名扩展或分词,但有参数扩展和命令替换。” - michael
Unix / Linux上的目录不应该有任何空格,随后脚本也不应该适应它。 Windows支持它已经足够糟糕,并且会对Windows脚本造成一切后果,但是,对于喜欢的东西,请不要引入不必要的要求。 - tvCa
@tvCa我发现用户通常更喜欢在目录名中更灵活,而不是被迫让开发人员更容易。 (事实上​​,在处理长文件名时,我发现没有空格的文件就像一个痛苦因为即使我自己在过去因为没有在脚本和程序中考虑带空格的路径而遭受包装而导致自动换行。) - JAB
哈。空格只是通常没有字形的字符。无论如何,你可以用反斜杠逃脱它们。 - uchuugaka


请注意 -d 测试可以产生一些令人惊讶的结果:

$ ln -s tmp/ t
$ if [ -d t ]; then rmdir t; fi
rmdir: directory "t": Path component not a directory

文件在:“什么时候目录不是目录?”答案是:“当它是目录的符号链接时。”一个稍微彻底的测试:

if [ -d t ]; then 
   if [ -L t ]; then 
      rm t
   else 
      rmdir t
   fi
fi

您可以在Bash手册中找到更多信息 Bash条件表达式 和 [ 内置命令 和 [[ 复合命令


204
2017-09-12 20:26



或者,假设只需要处理目录(并且可以忽略链接)=> if [ -d tmpdir -a ! -L tmpdir ]; then echo "is directory"; rmdir tmpdir; fi ...或者,对于一个适用于链接和目录的命令: rm -r tmpdir - michael


我找到了 双托架 的版本 test 使编写逻辑测试更自然:

if [[ -d "${DIRECTORY}" && ! -L "${DIRECTORY}" ]] ; then
    echo "It's a bona-fide directory"
fi

198
2017-09-12 21:33



对于 if [[ -d "$TARFILE" ]] 我得到[[:没找到 - TheVillageIdiot
同上[[:未找到 - Hedgehog
@TheVillageIdiot和@Hedgehog,你使用的是bash shell吗?双支架不是普遍支持的。这是关于这一点的答案: stackoverflow.com/questions/669452/... - yukondude
并在Busybox ash中使用默认编译选项 [[ ]] 支持,但实际上并没有提供任何不同的功能 [ ]。如果可移植性是一个问题,坚持下去 [ ] 并使用必要的解决方法。 - dubiousjim
...如果在shell脚本中使用bash结构,脚本的第一行应该是:#!/ bin / bash(而不是#!/ bin / sh,ksh等) - michael


缩短形式:

[ -d "$DIR" ] && echo "Yes"

125
2017-09-12 21:08



这样做是这样的: if $dir is a dir, then echo "yes"?一点解释会有所帮助:) - Martijn
cmd && other 是一种常见的简写 if cmd; then other; fi  - 这适用于支持布尔逻辑的大多数编程语言,并且被称为 短路评估。 - tripleee
行为不一样 set -e (这是一个 shell编程最佳实践)。 - dolmen


要检查目录是否存在,您可以使用简单的if结构,如下所示:

if [ -d directory/path to a directory ] ; then
#Things to do

else #if needed #also: elif [new condition] 
# things to do
fi

你也可以做负面的

if [ ! -d directory/path to a directory ] ; then
# things to do when not an existing directory

注意:小心,在开口和闭合支架的两侧留下空的空间。

使用相同的语法,您可以使用:

-e: any kind of archive 

-f: file 

-h: symbolic link 

-r: readable file 

-w: writable file 

-x: executable file 

-s: file size greater than zero 

119
2018-04-08 02:50





if [ -d "$DIRECTORY" ]; then  
    # Here if $DIRECTORY exists  
fi

94
2018-04-15 08:07





您可以使用 test -d (看到 man test)。

-d file       如果文件存在且为目录,则为True。

例如:

test -d "/etc" && echo Exists || echo Does not exist

注意: test 命令与条件表达式相同 [ (看到: man [),因此它可以跨shell脚本移植。

[  - 这是一个同义词 test 内置,但最后一个论点必须是文字 ],以匹配开幕 [

有关可能的选项或进一步帮助,请检查:

  • help [
  • help test
  • man test 要么 man [

74
2017-09-12 21:25





或者对于完全没用的东西:

[ -d . ] || echo "No"

55
2017-08-17 14:02



永远不会打印“否”。当前目录始终存在,除非被其他线程或其他方式删除。 - Jahid
完美的样本:)鉴于与接受的答案相比,“独特”是一些答案 - Sergey Sargsyan
为什么这么多次被投票?它没有回答这个问题。 - codeforester


这是一个非常实用的习语:

(cd $dir) || return # is this a directory,
                    # and do we have access?

我通常将它包装在一个函数中:

can_use_as_dir() { 
    (cd ${1:?pathname expected}) || return
}

要么:

assert_dir_access() { 
    (cd ${1:?pathname expected}) || exit
}

这种方法的好处是我不必考虑好的错误信息。

cd 将给我一个标准的一行消息给stderr已经。它还将提供比我能提供的更多信息。通过执行 cd 在子壳内 ( ... ),该命令不会影响调用者的当前目录。如果目录存在,则此子shell和函数只是一个无操作。

接下来是我们传递给的论点 cd${1:?pathname expected}。这是参数替换的更精细形式,下面将更详细地解释。

Tl; dr:如果传入此函数的字符串为空,我们再次退出子shell ( ... ) 并使用给定的错误消息从函数返回。


引自 ksh93 手册页:

${parameter:?word}

如果 parameter  设置并且为非null,然后替换其值;   否则,打印 word 并退出shell(如果不是交互式)。   如果 word 省略,然后打印标准消息。

如果结肠 : 从上面的表达式中省略,然后是   shell仅检查参数是否已设置。

这里的措辞是shell文档所特有的,如 word 可以参考 到任何合理的字符串,包括空格。

在这种特殊情况下,我知道标准错误消息 1: parameter not set 是不够的,所以我放大了我们期望的价值类型 - pathname 一个目录。

一个哲学说明: shell不是面向对象的语言,所以消息说 pathname不是 directory。在这个层面上,我宁愿保持简单 - 函数的参数只是字符串。


46
2017-08-09 21:43



这不仅仅是检查是否存在:在您的用户级别检查可访问性。所以问题代表 所有脑干 只要。所以正确答案是 test -d 正如@Grundlefleck解释的那样。 - F. Hauri
@ F.Hauri - 他没有要求更多,这是真的。但是,我发现我通常需要了解更多。 - Henk Langeveld
除非以root身份运行,否则我从未想过没有测试可以确定。 test -d /unreadable/exists 即使论证存在,也会失败。 - Henk Langeveld