일치 그룹 수가 다른 여러 패턴을 grep합니까?

일치 그룹 수가 다른 여러 패턴을 grep합니까?

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

위의 구조는 단지 3열 구조입니다. 첫 번째 열은 삽입 횟수, 두 번째 열은 삭제 횟수, 마지막 열은 파일 이름입니다.

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

추가 링크:git-diff 매뉴얼 페이지/기타 diff 형식

답변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개 삽입"을 추가하면 추가가 발생합니다. "삼".

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가 있는 경우 새 awk를 구입하십시오. 그러나 어떤 이유로 이것이 가능하지 않은 경우 and to [[:digit:]]로 변경 하면 모든 awk에서 작동합니다(물론 오래된 깨진 awk는 제외).[0-9][[:space:]][ \t]

관련 정보