用于查找计算机上所有空 git 存储库的脚本

用于查找计算机上所有空 git 存储库的脚本

.git我们知道一个空的 git 存储库中只有目录。

我想找到一台机器上所有空的 git 存储库。

我思考了这个过程:

  1. 查找所有指定的目录.git
  2. 如果它们在内部则将其排除Trash
  3. 如果其他人不是您的存储库,请排除它们(某些第三方应用程序也会拉取 git 存储库)
  4. 循环它们
  5. 对于每个存储库的父目录,计算其顶级文件和文件夹的数量
  6. 如果计数为零(不包括.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子程序中使用的是wantedperl内置的grep函数。这是不是grep 外部命令。 perl 的grep函数接受一个列表(数组)作为输入并返回另一个列表,其中代码块的计算结果为 true。在列表上下文中,该readdir函数返回目录中的文件名列表。参见perldoc -f grepperldoc -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

现在使脚本可执行并运行它。它打印以下目录的名称:

  1. 不是 Trash、Temp 或 opt 目录的子目录,
  2. 包含 .git 子目录,并且
  3. 不包含任何其他文件

./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 目录执行一次shand ls(也许还pwd)一次,如果有很多 .git 目录,这可能会增加很大的开销。

相关内容