关于Unix:检查shell脚本中是否存在目录

Check if a directory exists in a shell script

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


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

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

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

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

但是,正如jon ericson指出的那样,如果不考虑到指向目录的符号链接也将通过此检查,那么后续命令可能无法按预期工作。例如,运行此:

1
2
3
4
ln -s"$ACTUAL_DIR""$SYMLINK"
if [ -d"$SYMLINK" ]; then
  rmdir"$SYMLINK"
fi

将生成错误消息:

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

因此,如果后续命令需要目录,则符号链接可能必须以不同的方式处理:

1
2
3
4
5
6
7
8
9
10
11
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在另一个答案中解释了这一点。

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


在中引用变量时,请记住始终用双引号括起来。一个BASH脚本。现在的孩子们都是这样成长的:他们的目录名中可以有空格和许多其他有趣的字符。(空间!在我的时代,我们没有美好的空间!;)

有一天,其中一个孩子会运行你的脚本,把$DIRECTORY设置为"My M0viez",你的脚本就会爆炸。你不想那样。所以用这个。

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


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

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

文件位于:"什么时候目录不是目录?"答案是:"当它是一个指向目录的符号链接时。"一个稍微更彻底的测试:

1
2
3
4
5
6
7
if [ -d t ]; then
   if [ -L t ]; then
      rm t
   else
      rmdir t
   fi
fi

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


我发现test的双括号版本使编写逻辑测试更加自然:

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


较短形式:

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


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

1
2
3
4
5
6
if [ -d directory/path to a directory ] ; then
#Things to do

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

你也可以用否定的方式

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

注意:请小心,在左右两侧的大括号上都留有空格。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
-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

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

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

-d file True if file exists and is a directory.

例如:

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

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

[ - This is a synonym for the test builtin, but the last argument must, be a literal ], to match the opening [.

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

  • help [
  • help test
  • man testman [

  • 测试dir或file是否存在的简单脚本:

    1
    2
    3
    4
    5
    6
    if [ -d /home/ram/dir ]   # for file"if [-f /home/rama/file]"
    then
        echo"dir present"
    else
        echo"dir not present"
    fi
  • 检查目录是否存在的简单脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    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

    上面的脚本将检查dir是否存在

    如果最后一个命令成功,则返回"0",否则返回非零值。假设tempdir已经存在,那么mkdir tempdir将给出如下错误:

    mkdir: cannot create directory ‘tempdir’: File exists


  • 或者完全无用的东西:

    1
    [ -d . ] || echo"No"


    这里有一个非常实用的习语:

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

    我通常用一个函数包装它:

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

    或:

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

    这种方法的好处在于,我不必考虑好的错误消息。

    cd会给我一条标准的单行消息,发送给stderr。它还将提供我无法提供的更多信息。通过在子shell ( ... )中执行cd命令,该命令不会影响调用方的当前目录。如果目录存在,这个子shell和函数只是一个no-op。

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

    tl;dr:如果传递给这个函数的字符串是空的,那么我们再次从子shell ( ... )中退出,并返回带有给定错误消息的函数。

    引用ksh93手册:

    1
    ${parameter:?word}

    If parameter is set and is non-null then substitute its value;
    otherwise, print word and exit from the shell (if not interactive).
    If word is omitted then a standard message is printed.

    If the colon : is omitted from the above expressions, then the
    shell only checks whether parameter is set or not.

    这里的措词是shell文档特有的,如word所指。任何合理的字符串,包括空格。

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

    哲学注释:shell不是面向对象的语言,因此消息称为pathname,而不是directory。在这个层次上,我宁愿保持简单——函数的参数只是字符串。


    1
    2
    3
    4
    if [ -d"$Directory" -a -w"$Directory" ]
    then
        #Statements
    fi

    上面的代码检查目录是否存在以及是否可写。


    在bash promt上键入此代码

    1
    2
    3
    if [ -d"$DIRECTORY" ]; then
      # if true this block of code will execute
    fi

    更多使用find的功能

    • 检查子目录中是否存在文件夹:

      1
      2
      3
      4
      5
      6
      found=`find -type d -name"myDirectory"`
      if [ -n"$found"]
      then
          # The variable 'found' contains the full path where"myDirectory" is.
          # It may contain several lines if there are several folders named"myDirectory".
      fi
    • 根据当前目录中的模式检查一个或多个文件夹是否存在:

      1
      2
      3
      4
      5
      found=`find -maxdepth 1 -type d -name"my*"`
      if [ -n"$found"]
      then
          # The variable 'found' contains the full path where folders"my*" have been found.
      fi
    • 两种组合。在下面的示例中,它检查当前目录中是否存在文件夹:

      1
      2
      3
      4
      5
      found=`find -maxdepth 1 -type d -name"myDirectory"`
      if [ -n"$found"]
      then
          # The variable 'found' is not empty =>"myDirectory"` exists.
      fi


    实际上,您应该使用几种工具来获得防弹方法:

    1
    2
    3
    4
    DIR_PATH=`readlink -f"${the_stuff_you_test}"` # Get rid of symlinks and get abs path
    if [[ -d"${DIR_PATH}" ]] ; Then # now you're testing
        echo"It's a dir";
    fi

    只要使用"${}",就不必担心空格和特殊字符。

    注意,[[]]并不像[]那样可移植,但由于大多数人使用的是现代版本的bash(毕竟,大多数人甚至不使用命令行-p),所以好处大于麻烦。


    你有没有考虑过在江户中做任何你想做的事情,而不是在你跳之前看?

    也就是说,如果您想在输入目录之前检查它是否存在,请尝试这样做:

    1
    2
    3
    4
    if pushd /path/you/want/to/enter; then
        # commands you want to run in this directory
        popd
    fi

    如果您给pushd的路径存在,您将进入它,它将与0一起退出,这意味着将执行语句的then部分。如果它不存在,就不会发生任何事情(除了一些输出说目录不存在,这对于调试来说可能是一个有帮助的副作用)。

    似乎比这更好,这需要重复你自己:

    1
    2
    3
    4
    5
    if [ -d /path/you/want/to/enter ]; then
        pushd /path/you/want/to/enter
        # commands you want to run in this directory
        popd
    fi

    同样的事情也适用于cdmvrm等。如果您在不存在的文件上尝试它们,它们会出错退出并打印一条消息,说明不存在,并且您的then块将被跳过。如果在确实存在的文件上进行尝试,命令将以0的状态执行并退出,允许执行then块。


    要检查多个目录,请使用以下代码:

    1
    2
    3
    if [ -d"$DIRECTORY1" ] && [ -d"$DIRECTORY2" ] then
        # Things to do
    fi


    检查目录是否存在,否则创建一个目录

    1
    [ -d"$DIRECTORY" ] || mkdir $DIRECTORY


    1
    [[ -d"$DIR" && ! -L"$DIR" ]] && echo"It's a directory and not a symbolic link"

    N.B: Quoting variables is a good practice.


    1
    [ -d ~/Desktop/TEMPORAL/ ] && echo"DIRECTORY EXISTS" || echo"DIRECTORY DOES NOT EXIST"

    这个答案总结成一个shell脚本

    实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    $ is_dir ~                          
    YES

    $ is_dir /tmp                        
    YES

    $ is_dir ~/bin                      
    YES

    $ mkdir '/tmp/test me'

    $ is_dir '/tmp/test me'
    YES

    $ is_dir /asdf/asdf                  
    NO

    # Example of calling it in another script
    DIR=~/mydata
    if [ $(is_dir $DIR) =="NO" ]
    then
      echo"Folder doesnt exist: $DIR";
      exit;
    fi

    伊斯迪尔

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    function show_help()
    {
      IT=$(CAT <<EOF

      usage: DIR
      output: YES or NO, depending on whether or not the directory exists.

      )
      echo"$IT"
      exit
    }

    if ["$1" =="help" ]
    then
      show_help
    fi
    if [ -z"$1" ]
    then
      show_help
    fi

    DIR=$1
    if [ -d $DIR ]; then
       echo"YES";
       exit;
    fi
    echo"NO";

    使用-e检查将检查文件,其中包括目录。

    1
    2
    3
    4
    if [ -e ${FILE_PATH_AND_NAME} ]
    then
        echo"The file or directory exists."
    fi


    根据乔纳森评论:

    如果您想创建目录,但它还不存在,那么最简单的方法是使用创建目录的mkdir -p—以及路径上丢失的任何目录—如果目录已经存在,则不会失败,因此您可以同时使用:

    1
    mkdir -p /some/directory/you/want/to/exist || exit 1

    1
    2
    3
    if [ -d"$DIRECTORY" ]; then
        # Will enter here if $DIRECTORY exists
    fi

    这不是完全正确的…如果要转到该目录,还需要对该目录具有执行权限。也许你还需要写权限。

    因此:

    1
    2
    3
    4
    5
    if [ -d"$DIRECTORY" ] && [ -x"$DIRECTORY" ] ; then
        # ... to go to that directory (even if DIRECTORY is a link)
        cd $DIRECTORY
        pwd
    fi
    1
    2
    3
    4
    5
    if [ -d"$DIRECTORY" ] && [ -w"$DIRECTORY" ] ; then
        # ... to go to that directory and write something there (even if DIRECTORY is a link)
        cd $DIRECTORY
        touch foobar
    fi


    ls命令与-l选项(长列表)一起返回有关文件和目录的属性信息。尤其是ls -l输出的第一个字符,通常是d-破折号。如果是d,所列的目录是确定的。

    只有一行中的以下命令将告诉您给定的ISDIR变量是否包含目录路径:

    1
    2
    3
    [[ $(ls -ld"$ISDIR" | cut -c1) == 'd' ]] &&
        echo"YES, $ISDIR is a directory." ||
        echo"Sorry, $ISDIR is not a directory"

    实际用途:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        [claudio@nowhere ~]$ ISDIR="$HOME/Music"
        [claudio@nowhere ~]$ ls -ld"$ISDIR"
        drwxr-xr-x. 2 claudio claudio 4096 Aug 23 00:02 /home/claudio/Music
        [claudio@nowhere ~]$ [[ $(ls -ld"$ISDIR" | cut -c1) == 'd' ]] &&
            echo"YES, $ISDIR is a directory." ||
            echo"Sorry, $ISDIR is not a directory"
        YES, /home/claudio/Music is a directory.

        [claudio@nowhere ~]$ touch"empty file.txt"
        [claudio@nowhere ~]$ ISDIR="$HOME/empty file.txt"
        [claudio@nowhere ~]$ [[ $(ls -ld"$ISDIR" | cut -c1) == 'd' ]] &&
            echo"YES, $ISDIR is a directory." ||
            echo"Sorry, $ISDIR is not a directoy"
        Sorry, /home/claudio/empty file.txt is not a directory


    1
    2
    file="foo"
    if [[ -e"$file" ]]; then echo"File Exists"; fi;

    (1)

    1
    [ -d Piyush_Drv1 ] && echo""Exists"" || echo"Not Exists"

    (2)

    1
    [ `find . -type d -name Piyush_Drv1 -print | wc -l` -eq 1 ] && echo Exists || echo"Not Exists"

    (3)

    1
    [[ -d run_dir  && ! -L run_dir ]] && echo Exists || echo"Not Exists"

    如果发现上述方法之一存在问题。

    使用ls命令;当目录不存在时,会显示一条错误消息。

    $[[ls -ld SAMPLE_DIR| grep ^d | wc -l-公式1]]&;&;echo exists not exists-ksh:not:未找到[没有此类文件或目录]


    如果要检查目录是否存在,无论它是真正的目录还是符号链接,请使用以下命令:

    1
    2
    3
    4
    5
    6
    ls $DIR
    if [ $? != 0 ]; then
            echo"Directory $DIR already exists!"
            exit 1;
    fi
    echo"Directory $DIR does not exist..."

    说明:如果目录或符号链接不存在,则"ls"命令会给出错误"ls:/x:没有这样的文件或目录",并设置返回代码,您可以通过"$?"检索该返回代码。,非空(通常为"1")。请确保在调用"ls"后直接检查返回代码。


    很好的解决方案,但是如果您不在正确的目录中,最终每个脚本都会失败。所以代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    只有在执行时,您所在的目录正好有一个子目录要检查,才会成功执行。

    我理解这样的初始问题:验证不管用户在文件系统中的位置如何,目录是否存在。因此,使用"find"命令可能会做到这一点:

    1
    2
    3
    4
    dir=""
    echo"Input directory name to search for:"
    read dir
    find $HOME -name $dir -type d

    这个解决方案很好,因为它允许使用通配符,这是搜索文件/目录时的一个有用功能。唯一的问题是,如果搜索到的目录不存在,"find"命令将不打印任何内容到stdout(对于我来说这不是一个优雅的解决方案),并且仍然有一个零出口。也许有人可以改进这个。


    可以使用下面的find,

    1
    find . -type d -name dirname -prune -print

    Git Bash+Dropbox+窗口:

    其他解决方案都不适用于我的Dropbox文件夹,这很奇怪,因为我可以推到Dropbox符号路径。

    1
    2
    3
    4
    5
    6
    7
    8
    #!/bin/bash

    dbox="~/Dropbox/"
    result=0
    prv=$(pwd) && eval"cd $dbox" && result=1 && cd"$prv"
    echo $result

    read -p"Press Enter To Continue:"

    您可能还想知道如何从bash成功地导航到Dropbox。这是完整的剧本。

    https://pastebin.com/qf2exmpn


    file项目怎么样?考虑到所有目录也是Linux中的文件,发出以下命令就足够了:

    file $directory_name

    检查不存在的文件:file blah

    输出:cannot open 'blah' (No such file or directory)

    正在检查现有目录:file bluh

    输出:bluh: directory


    以三元形式,

    1
    [ -d"$directory" ] && echo"exist" || echo"not exist"

    随试

    1
    test -d"$directory" && echo"exist" || echo"not exist"

    从脚本文件myscript.sh:

    1
    2
    3
    4
    if [ -d /home/ec2-user/apache-tomcat-8.5.5/webapps/Gene\\ Directory ]; then
       echo"Directory exists!"
       echo"Great"
    fi

    1
    2
    3
    4
    if [ -d '/home/ec2-user/apache-tomcat-8.5.5/webapps/Gene Directory' ]; then
       echo"Directory exists!"
       echo"Great"
    fi