如何测试 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/folder
和 zip /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 值。
在 /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/folder
和 zip /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 值。