如何测试 Linux 目录是否只有一个子目录而没有其他文件?

How to test if a Linux directory contain only one subdirectory and no other files?

/bin/sh 脚本中,我想检查一个目录是否只包含一个子目录而没有其他文件(当然除了“.”和“..”)。我可能可以解析 ls 的输出,但我也明白这通常是个坏主意。建议?

提问原因:当我 zip 一个文件夹,比如说,一台 windows 机器,我将它解压缩到 Linux 下,有时我得到一个目录,其内容是原始文件夹;有时我会得到一个只包含一个子目录的目录,其内容是原始文件夹的内容。 (我假设我在 Windows 下使用 zip 的方式有所不同,或者我使用的各种 windows 机器的配置略有不同,或者......谁知道呢? ) Anhow,我想,在 Linux 方面,以或多或少相同的方式处理两种结果,因此这个问题。

对于那些思考 "What if your Windows-side folder really did contain just one subdir?" 的人来说,碰巧在这种情况下没关系,尽管我承认这是问题规范的一个极端情况。

您在 /bin/sh 中最大的问题是文件不可见,因为 * [默认情况下] 无法捕获。

这会做你想做的,我想:

#!/bin/bash
count_()
{
    echo $(( $# - 2 )) # -2 to remove . and ..
}
count()
{
    count_ * .*
}
ITEM_COUNT=$(count)

当然,如果您愿意,您可以调整它以将路径作为参数。

示例输出:

bash-3.2$ count
3
bash-3.2$ ll
total 0
drwxr-xr-x   5 christopher  wheel  170 Mar 18  2014 .
drwxrwxrwx   6 root         wheel  204 Jul  5 12:28 ..
drwxr-xr-x  14 christopher  wheel  476 Mar 18  2014 .git
drwxr-xr-x   5 christopher  wheel  170 Mar 18  2014 bin
drwxr-xr-x   4 christopher  wheel  136 Mar 18  2014 pylib

另一个例子:

sh-3.2$ count_()
> {
>     echo $(( $# - 2 )) # -2 to remove . and ..
> }
sh-3.2$ count()
> {
>     count_ * .*
> }
sh-3.2$ ITEM_COUNT=$(count)
sh-3.2$ echo ${ITEM_COUNT}
3

旁注:

你说得对,不同的 zip 实现处理方式不同,但在 Linux 上,许多工具对 zip /path/to/folderzip /path/to/folder/ 的处理方式不同 (这太荒谬了)。如果您在受控环境中工作,您可能希望规范化压缩方式。但是,如果这是面向用户的事情,那就糟透了。

如果您没有使用 bash 作为调用 shell:

countFiles.sh:

#!/bin/bash
count_()
{
    echo $(( $# - 2 )) # -2 to remove . and ..
}
count_ * .*

scriptThatWantsTheCountOfFiles:

#!/bin/tcsh
set count = `./countFiles.sh`

find 将是一个很好的工具。它有一些简洁的参数:

  • -maxdepth 1 所以它不会递归搜索
  • -type d 只搜索目录
  • -printf 1 解决奇怪文件名的问题(打印 1 而不是文件名)

完整的命令是:

find DIRECTORY -maxdepth 1 -type d -printf 1

这将为每个目录加上目录本身打印一个字符,因此您正在寻找打印两个字符的目录(查找有一个很好的功能,可以在搜索时忽略 ...)。

然后,你要检查是否有其他(非目录)文件:

find DIRECTORY -maxdepth 1 ! -type d -printf 1

完整的检查将是:

if [ "$(find DIRECTORY -maxdepth 1 -type d -printf 1 | wc -m)" -eq 2 \
        -a "$(find DIRECTORY -maxdepth 1 ! -type d -printf 1 | wc -m)" -eq 0 ]; then
    # It has only one subdirectory and no other content
fi

或者,您可以使用 -printf%y 使其成为一个命令,它打印文件类型(d 表示目录):

if [ "$(find DIRECTORY -maxdepth 1 -printf %y)" = "dd" ]; then
    # It has only one subdirectory and no other content
fi

为避免必须自己解析输出,您可以使用 find

if [[ `find . -type d | wc -l` -eq 2 && \
      `find . -type f | wc -l` -eq 0 ]]; then

  echo yes

else

  echo no

fi

如果要避免递归进入目录,可以指定-maxdepth 1.

有关其他一些选项,请参阅 man find

目录inode 的链接数多少有些帮助,但不能完全解决问题。一个目录有 2 + n 个指向它的链接,即 n 个在其中创建的子目录的数量。因此,如果您尝试查找所有只有 3 个链接的目录,您将得到其中只有一个子目录的目录。但是获得 no files 目录的唯一解决方案是搜索它。

也许您可以考虑一个混合的两阶段解决方案,首先您会找到所有带有 3 链接指向它们的目录(非常有效)。然后你只读那些,发现里面没有普通文件。

这是一个可移植的 shell 函数,已在 Dash 中测试:

check() {
    d=
    for i in ""/* ""/.* 
    do
        test ddd != "$d" && test -d "$i" || return 1
        d=d$d
    done
    test ddd = "$d"
}

我们使用变量 d 来计算我们已经看到了多少个目录。我们期待三个(... 和目标目录)。如果我们已经看到三个,那就早点离开;同样,如果我们看到一个非目录。

如果我们到达循环的末尾,我们会检查我们是否看到了三个目录; test 的 return 值成为函数的 return 值。