ChatGPT解决这个技术问题 Extra ChatGPT

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

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


P
Peter Mortensen

要检查 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 in another answer 解释了其原因。

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


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

在 Bash 脚本中引用变量时,请记住始终将变量用双引号括起来。这些天来,孩子们长大后认为他们的目录名称中可以有空格和许多其他有趣的字符。 (空间!回到我的时代,我们没有花哨的空间!;))

有一天,其中一个孩子会在将 $DIRECTORY 设置为 "My M0viez" 的情况下运行您的脚本,然后您的脚本就会崩溃。你不想要那个。所以用这个。

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

使用双引号的另一个原因是 $DIRECTORY 由于某种原因未设置。
“总是将变量用双引号括起来……在 bash 脚本中。”对于 bash,使用 [[...]] 时在技术上不是必需的;参见tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS(注意:没有分词):“[[ 和 ]] 之间没有文件名扩展或分词,但有参数扩展和命令替换。”
Unix/Linux 上的目录不应该有任何空格,随后的脚本也不应该适应它。 Windows 支持它已经够糟糕了,这对 Windows 脚本产生了所有后果,但是出于对任何事物的热爱,请不要引入不必要的要求。
@tvCa 我发现用户通常更喜欢允许他们的目录名称具有更大的灵活性,而不是被迫让开发人员的事情变得更容易。 (事实上,在处理长文件名时,我发现没有空格的文件名很痛苦,因为这会扼杀自动换行,尽管我自己过去曾因不考虑脚本和程序中带有空格的路径而受苦。)
哈。空格只是通常没有字形的字符。无论如何,你可以用反斜杠转义它们。
J
Jonathan Leffler

请注意 -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 conditional expressions[ builtin command[[ compound commmand 的更多信息。


或者,假设只需要在目录上工作(并且可以忽略链接)=> if [ -d tmpdir -a ! -L tmpdir ]; then echo "is directory"; rmdir tmpdir; fi ...或者,对于一个同时适用于两个链接的命令 &目录:rm -r tmpdir
J
Jay

我发现 testdouble-bracket 版本使编写逻辑测试更加自然:

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

对于 if [[ -d "$TARFILE" ]],我得到 [[: not found
@TheVillageIdiot 和@Hedgehog,您使用的是 bash shell 吗?双括号不是普遍支持的。这是关于这一点的 SO 答案:stackoverflow.com/questions/669452/…
在 Busybox ash 中,默认编译选项 [[ ]] 受支持,但实际上与 [ ] 没有任何不同的功能。如果担心可移植性,请坚持使用 [ ] 并使用必要的变通方法。
...如果在 shell 脚本中使用 bash 结构,则脚本的第一行应该是:#!/bin/bash(而不是 #!/bin/sh、ksh 等)
在 bash 中使用方括号时,不需要引用变量。
L
Lucas

较短的形式:

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

这是否像这样工作:if $dir is a dir, then echo "yes"?一点解释会有所帮助:)
cmd && otherif cmd; then other; fi 的常用简写 - 它适用于大多数支持布尔逻辑的编程语言,称为 short-circuit evaluation
set -e(即 shell programming best practice)下的行为不同。
@dolmen 检查了 [ -d "$DIR" ](后面是 && echo Yes),所以我相信 set -e 对脚本行为没有影响(即,如果测试失败,脚本会正常继续)。
@dolmen 这是不正确的。 set -e 下的行为完全相同。除了如果 echo 失败,脚本将退出。如果测试失败(即目录不存在,或 $DIR 存在但不是目录),单独的 [ -d "$DIR" ](没有 && cmd ...)将导致脚本在 set -e 下退出。
J
James Brown

一个测试目录或文件是否存在的简单脚本: if [ -d /home/ram/dir ] # For file "if [ -f /home/rama/file ]" then echo "dir present" else echo "dir not present" fi 一个简单的脚本来检查目录是否存在: mkdir tempdir # 如果你想检查文件使用 touch 而不是 mkdir ret=$? if [ "$ret" == "0" ] then echo "dir present" else echo "dir not present" fi 以上脚本将检查目录是否存在 $?如果最后一个命令成功,则返回“0”,否则返回非零值。假设 tempdir 已经存在。然后 mkdir tempdir 将给出如下错误: mkdir: cannot create directory 'tempdir': File exists


第二个将创建目录,如果它一开始不存在的话。那么它不是幂等的。
👆👆👆。第二个好像很危险。由于它创建了目录,因此该目录不存在甚至不再是真的。
mkdir 也不会创建完整路径(没有参数),但相反会更危险,因为您甚至无法还原(rm -f)更改,因为您不知道它创建了哪些目录
P
Peter Mortensen

要检查目录是否存在,您可以使用如下简单的 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 年接受的答案有什么好处?
更好,因为 [ ! -d 目录/目录路径]
k
kenorb

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

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

例如:

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

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

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

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

帮助 [

帮助测试

人测试或人[


s
sth

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

[ -d . ] || echo "No"

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

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

(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}。这是一种更复杂的参数替换形式,下面将更详细地解释。

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

引用 ksh93 手册页:

${parameter:?word}

如果参数已设置且非空,则替换其值;否则,打印 word 并从 shell 退出(如果不是交互式的)。如果 word 被省略,则打印一条标准消息。

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

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

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

哲学笔记:

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


这不仅仅是检查存在性:在您的用户级别检查可访问性。所以问题只代表存在。正如@Grundlefleck 解释的那样,正确的答案是test -d
@F.Hauri - 他没有要求更多,这是真的。然而,我发现我通常需要知道的远不止这些。
我从来没有想过没有任何测试可以是决定性的,除非它以 root 身份运行。 test -d /unreadable/exists 将失败,即使参数存在。
P
Peter Mortensen
if [ -d "$Directory" -a -w "$Directory" ]
then
    #Statements
fi

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


-a 在效果上与 -e 相同。它已被“弃用”,不鼓励使用。
V
Vishal
DIRECTORY=/tmp

if [ -d "$DIRECTORY" ]; then
    echo "Exists"
fi

Try online


[ 之后记住 space -> [` `-d。由于缺少空间,我得到了错误
AGAIN,这个答案在 2008 年就已经给出了,有更多有用的解释。这里唯一的新事物是在线游乐场。
f
faho

使用 find 的更多功能

检查子目录中的文件夹是否存在: found=`find -type d -name "myDirectory"` if [ -n "$found" ] then # 变量 'found' 包含 "myDirectory" 所在的完整路径。 # 如果有多个名为“myDirectory”的文件夹,它可能包含几行。菲

根据当前目录中的模式检查一个或多个文件夹的存在: found=`find -maxdepth 1 -type d -name "my*"` if [ -n "$found" ] then # The variable 'found' contains找到文件夹“my*”的完整路径。菲

两种组合。在下面的例子中,它检查当前目录中的文件夹是否存在: found=`find -maxdepth 1 -type d -name "myDirectory"` if [ -n "$found" ] then # The variable 'found' is不为空 => "myDirectory"` 存在。菲


嗨尼尔。您的想法可能有助于根据以下模式检查目录是否存在:find -maxdepth 1 -type d -name 'pattern'。你介意我在你的答案中附加这个技巧吗?干杯;)
P
Peter Mortensen

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

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),因此好处大于麻烦。


P
Peter Mortensen

您是否考虑过在 if 中做任何您想做的事情,而不是在跳跃之前先看?

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

if pushd /path/you/want/to/enter; then
    # Commands you want to run in this directory
    popd
fi

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

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

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 块执行。


pushd 对我来说是最优雅的方式。我正要发布它作为答案:)
J
Jahid
[[ -d "$DIR" && ! -L "$DIR" ]] && echo "It's a directory and not a symbolic link"

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

解释:

-d:检查它是否是一个目录

-L:检查是否是符号链接


解释将是有序的(通过 editing your answer,不在评论中)。
如果您使用 Bash 的 [[...]],则几乎不需要引用变量(即使是带有路径和元字符的空格)。一个例外是 = 的 RHS,例如 [[ $VAR = "$VAR" ]]。 (这是由于 Bash 将 RHS 解释为一个 glob。)
r
rbs

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

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

你怎么能检查它不存在?
P
Peter Mortensen

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

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

您可以使用 mkdir -p "$DIRECTORY" 来获得相同的效果。
mkdir 部分也需要在 $DIRECTORY 周围加上双引号。否则,分词可能会导致不良结果。例如:dir="a b"; mkdir $dir 将导致创建两个目录 ab,而不是单个 a b 目录。
d
doubleDown
[ -d ~/Desktop/TEMPORAL/ ] && echo "DIRECTORY EXISTS" || echo "DIRECTORY DOES NOT EXIST"

解释将是有序的(通过 editing your answer,不在评论中)。
d
doubleDown

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

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

C
Community

This answer 封装为 shell 脚本

例子

$ 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

is_dir

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";

3
3 revs, 3 users 70%

根据 Jonathan 的评论:

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

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

P
Peter Mortensen
if [ -d "$DIRECTORY" ]; then
    # Will enter here if $DIRECTORY exists
fi

这并不完全正确……

如果要进入该目录,还需要对该目录具有执行权限。也许您还需要具有写入权限。

所以:

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

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

P
Peter Mortensen

ternary 形式,

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

并使用 test

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

H
Henk Langeveld

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

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

[[ $(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

+1,但是当 ISDIR 根本不存在时,您会收到错误消息以及诊断消息。
a
ajmartin
file="foo" 
if [[ -e "$file" ]]; then echo "File Exists"; fi;

解释将是有序的(通过 editing your answer,不在评论中)。
P
Peter Mortensen

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

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'可能会成功:

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

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


如果一个程序通过我的整个硬盘驱动器查找目录而不是礼貌地查看我当前的工作目录或使用我给它的绝对路径,我会感到被冒犯。您的建议对于名为 locate 的工具可能很好,但对其他任何东西都不好...
P
Peter Mortensen

可以使用下面的find

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

P
Peter Mortensen

(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: not: not found [没有这样的文件或目录]


“上面”指的是什么?此答案或以前的答案中的三个命令行? (请在 editing your answer 之前回复,不在评论中。在此先感谢。)。
P
Peter Mortensen

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

file $directory_name

检查不存在的文件:file blah

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

检查现有目录:file bluh

输出: bluh: directory


好主意,但是在这两种情况下文件命令总是返回 0 退出代码太糟糕了。此外,当找不到文件/目录时:(。