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

什么命令检查目录是否存在于Bash外壳脚本中?

3065637 次浏览

检查目录是否存在:

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

要检查目录是否不存在:

if [ ! -d "$DIRECTORY" ]; thenecho "$DIRECTORY does not exist."fi

但是,正如乔恩·埃里克森所指出的,如果您没有考虑到指向目录的符号链接也会通过此检查,则后续命令可能无法按预期工作。例如,运行这个:

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

将产生错误消息:

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

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

if [ -d "$LINK_OR_DIR" ]; thenif [ -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"fifi

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

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

使用find的更多功能

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

      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
  • 根据当前目录中的模式检查一个或多个文件夹的存在:

      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
  • 两种组合。在以下示例中,它检查当前目录中文件夹的存在:

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

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

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

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

if [ -d t ]; thenif [ -L t ]; thenrm telsermdir tfifi

您可以在Bash手册中找到更多关于bash条件表达式#0内置命令#1复合逗号的信息。

更短的形式:

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

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

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

在Bash脚本中引用变量时,始终将它们包装在双引号中。

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

现在的孩子们把空格和许多其他有趣的字符放在他们的目录名称中。(空格!在我那个时代,我们没有花哨的空格!)有一天,其中一个孩子会在$DIRECTORY设置为"My M0viez"的情况下运行你的脚本,你的脚本会爆炸。你不希望这样。所以使用双引号。

或者完全无用的东西:

[ -d . ] || echo "No"

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

DIR_PATH=`readlink -f "${the_stuff_you_test}"` # Get rid of symlinks and get abs pathif [[ -d "${DIR_PATH}" ]] ; Then # Now you're testingecho "It's a dir";fi

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

请注意,[[]]不如[]可移植,但由于大多数人使用现代版本的Bash(毕竟,大多数人甚至不使用命令行:-p),因此好处大于麻烦。

if [ -d "$DIRECTORY" ]; then# Will enter here if $DIRECTORY existsfi

这并非完全正确……

如果您想访问该目录,您还需要对该目录拥有执行权限。也许您还需要写入权限。

因此:

if [ -d "$DIRECTORY" ] && [ -x "$DIRECTORY" ] ; then# ... to go to that directory (even if DIRECTORY is a link)cd $DIRECTORYpwdfi

if [ -d "$DIRECTORY" ] && [ -w "$DIRECTORY" ] ; then# ... to go to that directory and write something there (even if DIRECTORY is a link)cd $DIRECTORYtouch foobarfi
if [ -d "$Directory" -a -w "$Directory" ]then#Statementsfi

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

ls命令与-l(长列表)选项一起返回有关文件和目录的属性信息。
特别是ls -l输出的第一个字符通常是d-(破折号)。在d的情况下,列出的那个肯定是目录。

以下命令仅在一行中就会告诉您给定的#0变量是否包含目录的路径:

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

实际用法:

    [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

那里有很棒的解决方案,但最终如果你不在正确的目录中,每个脚本都会失败。所以像这样的代码:

if [ -d "$LINK_OR_DIR" ]; thenif [ -L "$LINK_OR_DIR" ]; then# It is a symlink!# Symbolic link specific commands go hererm "$LINK_OR_DIR"else# It's a directory!# Directory command goes herermdir "$LINK_OR_DIR"fifi

只有当您在执行时位于具有您碰巧检查的子目录的目录中时,才会成功执行。

我理解最初的问题是这样的:无论用户在文件系统中的位置如何,都要验证目录是否存在。因此,使用命令“查找”可能会起到作用:

dir=" "echo "Input directory name to search for:"read dirfind $HOME -name $dir -type d

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

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

(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将给我一个标准的单行消息标准误差已经。它还将提供比我能够提供的更多的信息。通过在子shell( ... )中执行cd,该命令不会影响调用者的当前目录。如果目录存在,则此子shell和函数只是一个无操作。

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

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


引用ksh93手册页:

${parameter:?word}

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

如果上述表达式中省略了冒号:,则shell只检查参数是否设置。

这里的措辞是shell留档特有的,因为word可以指任何合理的字符串,包括空格。

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

哲学注解:

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

file="foo"if [[ -e "$file" ]]; then echo "File Exists"; fi;
[ -d ~/Desktop/TEMPORAL/ ] && echo "DIRECTORY EXISTS" || echo "DIRECTORY DOES NOT EXIST"

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

if [ -e ${FILE_PATH_AND_NAME} ]thenecho "The file or directory exists."fi

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

ls $DIRif [ $? != 0 ]; thenecho "Directory $DIR already exists!"exit 1;fiecho "Directory $DIR does not exist..."

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

下面的find可以使用。

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

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

if [ -d directory/path to a directory ] ; then# Things to do
else #if needed #also: elif [new condition]# Things to dofi

你也可以用否定的方式来做:

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
[[ -d "$DIR" && ! -L "$DIR" ]] && echo "It's a directory and not a symbolic link"

注意:引用变量是一种很好的做法。

说明:

  • -d:检查它是否是一个目录
  • -L:检查它是否是符号链接

(1)

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

(2)

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

(3)

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

如果使用上述方法之一发现问题:

使用ls命令;目录不存在的情况-显示错误消息

[[ `ls -ld SAMPLE_DIR| grep ^d | wc -l` -eq 1 ]] && echo exists || not exists

-ksh:不:找不到[没有这样的文件或目录]

乔纳森的评论:

如果您想创建目录,但它还不存在,那么最简单的技术是使用mkdir -p创建目录-以及路径上任何缺失的目录-如果目录已经存在,则不会失败,因此您可以一次完成所有操作:

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

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

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

例如:

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

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

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

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

  • help [
  • help test
  • man testman [

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

if [ -d "$DIRECTORY1" ] && [ -d "$DIRECTORY2" ] then# Things to dofi

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

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

这个答案包装为外壳脚本

示例

$ is_dir ~YES
$ is_dir /tmpYES
$ is_dir ~/binYES
$ mkdir '/tmp/test me'
$ is_dir '/tmp/test me'YES
$ is_dir /asdf/asdfNO
# Example of calling it in another scriptDIR=~/mydataif [ $(is_dir $DIR) == "NO" ]thenecho "Folder doesnt exist: $DIR";exit;fi

is_dir

function show_help(){IT=$(CAT <<EOF
usage: DIRoutput: YES or NO, depending on whether or not the directory exists.
)echo "$IT"exit}
if [ "$1" == "help" ]thenshow_helpfiif [ -z "$1" ]thenshow_helpfi
DIR=$1if [ -d $DIR ]; thenecho "YES";exit;fiecho "NO";

三元的形式,

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

关于#0

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

从脚本文件myScript.sh

if [ -d /home/ec2-user/apache-tomcat-8.5.5/webapps/Gene\ Directory ]; thenecho "Directory exists!"echo "Great"fi

if [ -d '/home/ec2-user/apache-tomcat-8.5.5/webapps/Gene Directory' ]; thenecho "Directory exists!"echo "Great"fi

Git Bash+Dropbox+Windows:

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

#!/bin/bash
dbox="~/Dropbox/"result=0prv=$(pwd) && eval "cd $dbox" && result=1 && cd "$prv"echo $result
read -p "Press Enter To Continue:"

您可能还想知道如何从Bash成功导航到Dropbox。所以这是完整的脚本。

https://pastebin.com/QF2Exmpn

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

即,如果您想在输入之前检查目录的存在,请尝试执行以下操作:

if pushd /path/you/want/to/enter; then# Commands you want to run in this directorypopdfi

如果你给pushd的路径存在,你将进入它,它将以0退出,这意味着语句的then部分将执行。如果它不存在,什么都不会发生(除了一些输出说目录不存在,这可能是一个有用的副作用,无论如何调试)。

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

if [ -d /path/you/want/to/enter ]; thenpushd /path/you/want/to/enter# Commands you want to run in this directorypopdfi

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

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

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

     mkdir tempdir   # If you want to check file use touch instead of mkdirret=$?if [ "$ret" == "0" ]thenecho "dir present"elseecho "dir not present"fi

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

    $?如果最后一个命令成功,则返回“0”,否则返回非零值。假设tempdir已经存在。然后mkdir tempdir会给出如下错误:

    mkdir:无法创建目录'Tempdir':文件存在

使用file程序。考虑到所有目录也是Linux中的文件,发出以下命令就足够了:

file $directory_name

检查不存在的文件:file blah

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

检查现有目录:file bluh

输出:bluh: directory

作为“[-d]”和“[-h]”选项的替代,您可以使用stat来获取文件类型并对其进行解析。

#! /bin/bashMY_DIR=$1NODE_TYPE=$(stat -c '%F' ${MY_DIR} 2>/dev/null)case "${NODE_TYPE}" in"directory") echo $MY_DIR;;"symbolic link") echo $(readlink $MY_DIR);;"") echo "$MY_DIR does not exist";;*) echo "$NODE_TYPE is unsupported";;esacexit 0

测试数据:

$ mkdir tmp$ ln -s tmp derp$ touch a.txt$ ./dir.sh tmptmp$ ./dir.sh derptmp$ ./dir.sh a.txtregular file is unsupported$ ./dir.sh godgod does not exist
DIRECTORY=/tmp
if [ -d "$DIRECTORY" ]; thenecho "Exists"fi

在线试用

一条线:

[[ -d $Directory ]] && echo true