如何在Bash shell脚本中检查目录是否存在?

4400

在Bash shell脚本中,哪个命令可以检查目录是否存在?

35个回答

5913

检查目录是否存在:

if [ -d "$DIRECTORY" ]; then
  echo "$DIRECTORY does exist."
fi

检查目录是否不存在:

if [ ! -d "$DIRECTORY" ]; then
  echo "$DIRECTORY does not 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的另一个答案中有解释。

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


42
如果你想在使用GNU工具时保险起见,强烈建议使用“--”(选项结束标志)。否则,如果你的变量包含类似选项的内容,脚本将会像空格一样失败。 - Marc Mutz - mmutz
4
对于现代版本的bash、ksh等,[...]是一个内建命令。 - fpmurphy
97
请注意:[ ! -d "$DIRECTORY" ] 的判断结果为真,不仅当 $DIRECTORY 不存在时,也包括 $DIRECTORY 存在但不是一个目录的情况。考虑使用类似 if [ ! -d "$DIRECTORY" ]; then mkdir "$DIRECTORY"; fi 的语句;如果"$DIRECTORY"是文件,则此语句将失败。(当然,您应该无论如何都检查 mkdir 是否成功;因为有许多原因可能导致它失败。) - Keith Thompson
10
值得一提的是,一旦检查完成,由于其他进程的影响,情况可能已经发生了改变。在许多情况下,最好是直接创建或使用目录,并对失败做出反应。 - Alfe
19
不必同时测试目录 (-d) 和符号链接 (-L),只需在变量后面加上斜杠即可更方便,例如 if [ -d "${THING:+$THING/}" ]。目录不会介意多余的斜杠,文件返回 false,空值保持为空值,也会返回 false。符号链接会被解析为它所指向的目标。当然,这取决于你的目的。如果你想要前往该目录,那么这样做是可以的。但如果你想删除它,那么这个答案中的代码更好。 - ghoti
显示剩余9条评论

613

在Bash脚本中引用变量时,始终将变量用双引号括起来。

if [ -d "$DIRECTORY" ]; then
    # Will enter here if $DIRECTORY exists, even if it contains spaces
fi
孩子们这些天在他们的目录名中加入空格和许多其他有趣的字符。(空格!在我那个年代,我们没有任何花哨的空格!) 有一天,其中一个孩子将运行您的脚本,并将$DIRECTORY设置为"My M0viez",然后您的脚本将崩溃。 您不想要那样。 所以请使用双引号。

18
使用双引号的另一个原因是,以防万一$DIRECTORY没有设置。 - Jon Ericson
6
在bash脚本中,始终将变量用双引号括起来。对于使用[[...]]的情况,这不是必需的技术操作;参见http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS(注意:在[[和]]之间没有单词分割或文件名扩展),但存在参数扩展和命令替换。 - michael
4
Unix/Linux系统上的目录名称不应该包含任何空格,因此脚本也不应该针对其进行调整。虽然Windows支持带空格的路径,但这已经够糟糕的了,会给Windows脚本带来很多问题。请不要引入不必要的要求,无论出于何种原因。 - tvCa
29
我发现用户通常更喜欢在目录名中获得更多的灵活性,而不是被迫使开发人员更容易操作。实际上,当处理长的文件名时,我发现没有空格的文件名很麻烦,因为这会破坏换行,尽管我自己曾经在脚本和程序中对带有空格的路径未予考虑而遭受过苦难。 - JAB
7
哈。空格通常只是没有字形的字符。无论如何,您可以用反斜杠转义它们。 - uchuugaka
显示剩余9条评论

263

请注意,-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条件表达式[内置命令[[复合命令的更多信息。


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

247
我发现使用双括号版本的test更自然地编写逻辑测试。
if [[ -d "${DIRECTORY}" && ! -L "${DIRECTORY}" ]] ; then
    echo "It's a bona-fide directory"
fi

有关-L和其他条件表达式的含义,请参阅{{link1:Bash参考文档:6.4 Bash条件表达式}}。

18
@TheVillageIdiot和@Hedgehog,你们在使用Bash Shell吗?双方括号不是通用支持的。这里有一个关于这一点的SO答案:https://dev59.com/NXRB5IYBdhLWcg3wSVi1#669486 - yukondude
9
在使用默认编译选项的 Busybox ash 中,支持 [[ ]] 语法,但实际上与 [ ] 没有任何功能上的不同。如果需要可移植性,请使用 [ ] 并使用必要的解决方法。 - dubiousjim
9
如果在shell脚本中使用bash命令,脚本的第一行应该是:#!/bin/bash(而不是#!/bin/sh、ksh等)。 - michael
5
在Bash中使用双方括号时,您无需引用变量。 - Hubert Grzeskowiak
@AdamKatz:-L已经被弃用了?在哪个系统中?Bash的help test,GNU coreutils手册或信息页面都没有提到这一点。 - Soren Bjornstad
显示剩余4条评论

223

简短形式:

# if $DIR is a directory, then print yes
[ -d "$DIR" ] && echo "Yes"

11
这个是否按照这样工作:如果$dir是一个目录,则回显“yes”?稍微解释一下会更有帮助 :) - Martijn
8
cmd && otherif cmd; then other; fi 的常用简写形式 -- 这适用于大多数支持布尔逻辑的编程语言,并被称为短路求值 - tripleee
8
set -e下(这是Shell编程最佳实践之一),行为不同。 - dolmen
3
@dolmen, [ -d "$DIR" ] 进行了检查(紧随其后是 && echo Yes),因此我认为 set -e 不会影响脚本的行为(即如果测试失败,脚本会继续正常运行)。 - tzot
@dolmen 那不正确。在 set -e 下,行为完全相同。除非 echo 失败,否则脚本将退出。[ -d "$DIR" ] 单独(没有 && cmd ...)将导致脚本在 set -e 下退出,如果测试失败(即目录不存在或 $DIR 存在但不是目录)。 - dan

153
  1. 一个简单的脚本,用于测试目录或文件是否存在:

     if [ -d /home/ram/dir ]   # For file "if [ -f /home/rama/file ]"
     then
         echo "dir present"
     else
         echo "dir not present"
     fi
    
  2. 一个简单的脚本,用于检查目录是否存在:

  3.  mkdir tempdir   # If you want to check file use touch instead of mkdir
     ret=$?
     if [ "$ret" == "0" ]
     then
         echo "dir present"
     else
         echo "dir not present"
     fi
    

    以上脚本将检查目录是否存在。

    $? 如果上一条命令成功执行,则返回“0”,否则为非零值。假设 tempdir 已经存在。然后执行 mkdir tempdir 将会出现以下错误:

    mkdir: 无法创建目录"tempdir": 文件已存在


8
如果目录在一开始不存在,第二种方法会创建该目录。因此它不是幂等的。 - Shihe Zhang
2
第二个似乎很危险。因为它创建了目录,所以现在不能再说该目录不存在了。 - Chris
mkdir命令在没有参数的情况下也无法创建完整路径,但相反的做法甚至更加危险,因为您甚至无法撤消(-f rm)所做的更改,因为您不知道它已经创建了哪些目录。 - venimus

146

要检查目录是否存在,您可以使用类似于以下的简单的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

除了离题的文件开关之外,这有什么比2008年被接受的答案更好的地方吗? - Dan Dascalescu
由于 [ ! -d 目录/目录路径 ],所以更好。 - user1855805

110

你可以使用test -d(参见man test)。

-d file 如果文件存在且是目录,则返回真。

例如:

test -d "/etc" && echo Exists || echo Does not exist
注意:`test` 命令和条件表达式 `[`(参见:man [)是相同的,因此在 shell 脚本中可移植。

[ - 这是 `test` 内置命令的同义词,但最后一个参数必须是文字 ],以匹配开头的 [

有关可能的选项或更多帮助,请使用以下命令:

  • help [
  • help test
  • man testman [

62

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

[ -d . ] || echo "No"

9
它永远不会打印“无”。当前目录始终存在,除非被另一个线程或其他方式删除。 - Jahid

61

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

(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命令会给我一个标准的一行消息standard error,它也会提供比我能提供更多的信息。通过在子shell ( ... ) 中执行cd,该命令不会影响调用者的当前目录。如果该目录存在,则此子shell和函数只是无操作。接下来是我们传递给cd的参数:${1:?pathname expected}。这是一种更复杂的参数替换形式,下面将详细解释。简而言之:如果传递到该函数的字符串为空,则我们再次从子shell ( ... )退出,并返回带有给定错误消息的函数。

引用自ksh93手册:

${parameter:?word}

如果设置了parameter并且它非空,则替换其值;否则,打印word并退出shell(如果不是交互式)。如果省略word,则会打印标准消息。
如果上述表达式中省略冒号:,则shell仅检查参数是否设置。
这里的措辞对于shell文档来说很奇怪,因为word可以引用任何合理的字符串,包括空格。
在这种特殊情况下,我知道标准错误消息1: parameter not set是不足够的,所以我关注我们在此处期望的值类型 - 目录的pathname
哲学笔记:
Shell不是面向对象的语言,因此消息说pathname而不是directory。在这个级别上,我更愿意保持简单 - 函数的参数只是字符串。

这不仅仅是检查存在性,它还检查您的用户级别的可访问性。SO问题仅代表存在性。因此,正确的答案是test -d,如@Grundlefleck所解释的。 - F. Hauri - Give Up GitHub
8
@F.Hauri - 他确实没有要求更多,但我发现通常我需要知道更多的信息。 - Henk Langeveld
我从未想过,除非以root身份运行,否则没有测试可以得出确定性结论。即使参数存在,“test -d /unreadable/exists”也会失败。 - Henk Langeveld

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接