我正在尝试统计 git diff 的插入和删除。
我有以下内容,当通过管道传输一个或多个形式的字符串时"4 files changed, 629607 insertions(+), 123 deletions(-)"
可以计算总数:
grep -Eo 'changed, ?(\d+) insertion.*(\d+) deletion' | awk '{ i+=$2; d+=$4 } END { print "insertions: ",i," deletions: ",d }'
这会产生insertions: 629607 deletions: 123
然而,有时 diff 并不遵循上述格式,而是只有插入或只有删除。
在这些情况下,我不需要匹配两个数字,而只需要匹配一个(并确保它最终出现在右列中)。
如何创建一个足够灵活的正则表达式来处理这些变化,并且生成awk
可以正确计数的输出?
答案1
我会Perl
在这里使用灵活性、可读性和可移植性,没有复杂的正则表达式,让我们KISS
(...我只使用一根管子git
)。
它在任何情况下都有效:有或没有模式之一,否则这将跳过根本不匹配的行:
$ git diff
7 insertions, 1 deletions
1 deletions
3 insertions
foobar
$ git diff | perl -nE '
BEGIN{our $insert = our $delete = 0}
$insert += $1 if /(\d+)\s+insertion/;
$delete += $1 if /(\d+)\s+deletion/;
END{say $insert . " insertions, " . $delete . " deletions"}
'
10 insertions, 2 deletions
答案2
其他发帖者已经回答了如何直接解决您的问题。但是,既然您提到您正在解析 的结果git diff
,我建议采用稍微不同的方法。
diff
如果你想在脚本中使用输出,你可以使用--numstat
代替--stat
。
您最终将得到一致的输出,因为其目的--numstat
是用于脚本。
使用时git diff --stat
,您会得到以下输出:
$ git diff main --stat
[...list of files...]
5 files changed, 112 insertions(+), 20 deletions(-)
使用时git diff --statnum
,您会得到以下输出:
$ git diff main --statnum
- - some/binary/file
15 0 some/file
1 1 some/other/file
29 7 another/file
67 12 yet/another/file
上面的结构只是一个三列结构。第一列是插入次数,第二列是删除次数,最后一列是文件名。
您可以通过管道传输命令来汇总列,awk
而不必担心是否有任何插入和/或删除。
$ git diff main --numstat | awk '{sum_insertions+=$1;sum_deletions+=$2}END{print "insertions:", sum_insertions+0, "deletions:", sum_deletions+0;}'
insertions: 112 deletions: 20
答案3
grep
并没有真正提供捕获组的良好视图,所以我在这里切换到 Perl。通过此测试输入foo.txt
:
2 files changed, 2 insertions(+), 7 deletions(-)
1 file changed, 9 deletions(-)
garbage
1 file changed, 10 insertions(+)
你可以这样做:
$ perl -ne '/files? changed, (?:(\d+) insertion\S*)? ?(?:(\d+) deletion)?/ && printf "%d %d\n", $1, $2' < foo.txt
2 7
0 9
10 0
或者也用 Perl 进行求和:
$ perl -ne 'if (/files? changed, (?:(\d+) insertion\S*)? ?(?:(\d+) deletion)?/) { $i += $1; $d += $2 } END { printf "insertions: %d deletions: %d\n", $i, $d }' < foo.txt
insertions: 12 deletions: 16
这里的要点是,用于插入的组在?
其后面是可选的,并且捕获组的编号从左到右,无论它们是否匹配。再加上一些捏造,以便所有三种可能的输入格式都匹配。当然,您也可以针对/, (\d+) insertion/
和进行两场单独的比赛/, (\d+) deletion/
,等等。
答案4
GNU awk 解决方案与 Perl 基本相同
gawk -F'\n' '
match($0, /([0-9]+)\s+insertion/, i) { total_i += i[1]; }
match($0, /([0-9]+)\s+deletion/, d) { total_d += d[1]; }
END {
printf("insertions: %d deletions: %d\n", total_i, total_d);
}
'
POSIX awk 没有捕获组,但为了避免任何额外的split()
步骤,可以依靠 awk 使用匹配字符串的初始数字部分,并在数字计算中自动删除其余部分,即添加“3 个插入”会导致添加“3 ”。
awk -F'\n' '
match($0, /[[:digit:]]+[[:space:]]+insertion/) {
total_i += substr($0, RSTART, RLENGTH)
}
match($0, /[[:digit:]]+[[:space:]]+deletion/) {
total_d += substr($0, RSTART, RLENGTH)
}
END {
printf "insertions: %d deletions: %d\n", total_i, total_d
}
'
如果你有一个 POSIX 之前的 awk,那么就买一个新的,但如果由于某种原因这是不可能的,那么更改[[:digit:]]
为[0-9]
和[[:space:]]
to [ \t]
,然后这将在任何 awk 中工作(当然,旧的损坏的 awk 除外)。