.git
我们知道一个空的 git 存储库中只有目录。
我想找到一台机器上所有空的 git 存储库。
我思考了这个过程:
- 查找所有指定的目录
.git
- 如果它们在内部则将其排除
Trash
- 如果其他人不是您的存储库,请排除它们(某些第三方应用程序也会拉取 git 存储库)
- 循环它们
- 对于每个存储库的父目录,计算其顶级文件和文件夹的数量
- 如果计数为零(不包括
.git
)或为 1(包括.git
),则 repo 为空。echo
它。
这是我的脚本:
find / -type d -name .git 2>/dev/null |
{
while read gitFolder; do
if [[ $gitFolder == *"/Temp/"* ]]; then
continue;
fi
if [[ $gitFolder == *"/Trash/"* ]]; then
continue;
fi
if [[ $gitFolder == *"/opt/"* ]]; then
continue;
fi
parent=$(dirname $gitFolder);
echo "";
if [ $(ls $parent -A | wc -l ) != 1 ]; then
echo $parent
fi
done
}
但这并没有像我预期的那样工作。它列出了所有的存储库,这意味着我在比较部分有一个错误,但我找不到。我做错了什么?
另外,我认为这可能不是最好的方法。关于如何让它变得更好有什么想法吗?
答案1
这并不能解释为什么你的比较不起作用,但你可以find
直接完成所有这些(如果你find
支持-execdir
),而无需循环其输出:
find / -type d \( \( \( -name Temp -o -name Trash -o -name opt \) -prune \) \
-o \( -name .git -execdir sh -c '[ "$(ls -A)" = ".git" ] && pwd' \; \) \)
这会查找所有目录、修剪Temp
等(因此它甚至不会探索它们的子目录)。当它找到名为 的目录时.git
,它会在父目录中运行测试以查看是否.git
是唯一存在的文件,如果是,则运行pwd
以打印当前目录。
答案2
$ cat find-empty-git.pl
#!/usr/bin/perl
use File::Find;
use List::Util qw(uniq);
push @ARGV, './' unless @ARGV;
@ARGV = uniq(@ARGV);
foreach (@ARGV) { die "$_ is not a directory" unless -d $_ };
find(\&wanted, @ARGV);
sub wanted {
$File::Find::prune = 1 if $File::Find::name =~ m=/(Trash|Temp|opt)($|/)=;
return unless (-d && /^\.git$/);
opendir(my $dh, '.') ||
warn "Can't open $File::Find::dir: $!" &&
return;
return if (grep { ! /^(\.{1,2}|\.git)$/ } readdir($dh));
closedir($dh);
print "$File::Find::dir\n";
}
此 perl 脚本使用在命令行上指定的目录名称作为要搜索的顶级目录(或多个目录)。
./
如果未指定目录,则默认为。它检查每个参数实际上是一个目录,并使用uniq()
来自列表::实用程序模块来消除重复的目录名称。这文件::查找module用于递归搜索指定目录。这两个模块都是核心 perl 模块并且包含在 perl 中(即它们不需要单独安装)。
对于找到的每个文件名,wanted
都会执行子例程。
/Trash
首先,它检查当前文件名的完整路径名是否以、/Temp
或结尾,/opt
或者 是其中之一的后代。如果是,它会从搜索树中删除该目录。
接下来,如果文件名不是目录并且不是.git
,则子例程立即返回。
$dh
否则,将打开包含该文件的目录(使用称为目录句柄的变量。请参阅perldoc -f opendir
)并检查该目录的内容。如果由于任何原因(例如权限)无法打开目录进行读取,则将其视为非致命错误(警告消息将打印到 stderr 并且子例程返回)。
grep
子程序中使用的是wanted
perl内置的grep函数。这是不是grep 外部命令。 perl 的grep
函数接受一个列表(数组)作为输入并返回另一个列表,其中代码块的计算结果为 true。在列表上下文中,该readdir
函数返回目录中的文件名列表。参见perldoc -f grep
和perldoc -f readdir
。
简而言之:return if grep... readdir($dh)
如果目录中存在任何与.
、..
或不匹配的“文件”,该行会从所需函数提前返回(即在打印目录名称之前) .git
。此处使用“文件”一词一般意义包括常规文件、符号链接、目录、设备节点、命名管道、套接字等。
最后,到此为止,将打印目录名称。
顺便说一句,如果您需要 NUL 分隔的目录名称列表而不是换行符分隔,print "$File::Find::dir\n";
则可以将该行更改为。print "$File::Find::dir\0";
样品运行。首先创建一个测试环境,创建一些目录(a、b 和 c),其中包含 .git 子目录。在这些目录之一中创建一个文件。创建另一个没有 .git 子目录但有 .git 子目录 (e) 的目录 (d)。以及 ./Trash/ 和 ./Temp/ 下的一些 .git 子目录
$ mkdir -p {a,b,c}/.git/
$ touch a/file1
$ mkdir -p d/e/.git
$ mkdir -p Trash/f/.git Temp/g/.git
$ tree --metafirst --noreport -paf a b c d Trash Temp
[drwxr-xr-x] a
[-rw-r--r--] ├── a/file1
[drwxr-xr-x] └── a/.git
[drwxr-xr-x] b
[drwxr-xr-x] └── b/.git
[drwxr-xr-x] c
[drwxr-xr-x] └── c/.git
[drwxr-xr-x] d
[drwxr-xr-x] └── d/e
[drwxr-xr-x] └── d/e/.git
[drwxr-xr-x] Trash
[drwxr-xr-x] └── Trash/f
[drwxr-xr-x] └── Trash/f/.git
[drwxr-xr-x] Temp
[drwxr-xr-x] └── Temp/g
[drwxr-xr-x] └── Temp/g/.git
现在使脚本可执行并运行它。它打印以下目录的名称:
- 不是 Trash、Temp 或 opt 目录的子目录,
- 包含 .git 子目录,并且
- 不包含任何其他文件
即./b
和。./c
./d/e
$ chmod +x ./find-empty-git.pl
$ ./find-empty-git.pl ./
./b
./d/e
./c
顺便说一句,根据您阅读和使用相当线性代码的舒适程度,这可能会或可能不会比中等长度和复杂的find
命令行更容易阅读和理解。这对我来说当然更容易(但这可能是因为File::Find
在过去的几十年里我已经编写了数十个类似的基于 Little 的脚本)。
很难说这是否会比跑步更快find
。大概。或许。取决于您有多少个包含 .git 子目录的目录。使用此脚本,perl
只需运行一次,并且不执行任何外部程序。 Stephen 的find
命令必须对它找到的每个 .git 目录执行一次sh
and ls
(也许还pwd
)一次,如果有很多 .git 目录,这可能会增加很大的开销。