모든 .txt 파일의 총 줄 수를 계산하는 방법은 무엇입니까?

모든 .txt 파일의 총 줄 수를 계산하는 방법은 무엇입니까?

모든 .txt 파일에서 총 줄 수를 가져오는 방법을 알아내려고 합니다. 문제는 6 -> 줄에 있다고 생각합니다 let $((total = total + count )). 혹시 이것의 올바른 형태가 무엇인지 아시는 분 계신가요?

#!/bin/bash
total=0
find /home -type f -name "*.txt" | while read -r FILE; do
          count=$(grep -c ^ < "$FILE")
           echo "$FILE has $count lines"
           let $((total = total + count ))
        done
        echo TOTAL LINES COUNTED:  $total

감사해요

답변1

6행은 다음과 같이 작성하는 것이 더 좋습니다.

total=$(( total + count ))

...하지만 도구를 사용하는 것이 더 좋습니다생산줄 수 계산(개행 수, 즉 올바르게 종료된 줄을 계산한다고 가정)

find . -name '*.txt' -type f -exec cat {} + | wc -l

이는 현재 디렉토리 또는 그 아래에 있는 파일을 찾습니다 .txt. 이 모든 파일은 스트림으로 연결되어 으로 파이프되어 wc -l질문의 제목과 텍스트에서 요구하는 총 줄 수를 출력합니다.

전체 스크립트:

#!/bin/sh

nlines=$( find . -name '*.txt' -type f -exec cat {} + | wc -l )

printf 'Total number of lines: %d\n' "$nlines"

각 파일의 줄 수를 동시에 얻으려면 다음을 고려하십시오.

find . -name '*.txt' -type f -exec sh -c '
    wc -l "$@" |
    if [ "$#" -gt 1 ]; then
        sed "\$d"
    else
        cat
    fi' sh {} + |
awk '{ tot += $1 } END { printf "Total: %d\n", tot }; 1'

이는 wc -l배치 파일을 호출하여 각 개별 파일의 줄 수를 출력합니다. 여러 파일 이름으로 호출 하면 wc -l끝에 총 개수가 포함된 줄이 출력됩니다. 인라인 스크립트가 여러 파일 이름 인수를 사용하여 호출되는 sed경우 이 줄을 제거합니다 .sh -c

그런 다음 줄 수와 파일 경로 이름의 긴 목록을 전달하면 awk개수를 추가하고(데이터를 전달하고) 마지막에 총 개수를 사용자에게 표시합니다.


GNU 시스템에서 이 wc도구는 널로 구분된 스트림에서 경로 이름을 읽을 수 있습니다. 다음과 같이 이러한 시스템에서 이 기능 find과 해당 작업을 사용할 수 있습니다.-print0

find . -name '*.txt' -type f -print0 |
wc --files0-from=- -l

wc여기서 발견된 경로 이름은 비표준을 사용하기 위해 빈 구분 목록으로 파이프됩니다 -print0. 이 wc유틸리티 --files0-from는 파이프를 통해 전달된 목록을 읽기 위해 비표준 옵션과 함께 사용됩니다.

답변2

let $((total = total + count ))

이것은 작동하지만 둘 다 산술 확장을 let시작하기 때문에 약간 중복됩니다 .$(( .. ))

let "total = total + count", 또는 중 하나 라도 중복 없이 이를 let "total += count"수행 할 수 있습니다 . 마지막 두 개는 표준 케이스와 호환되어야 하지만 그렇지 않습니다.: $((total = total + count))total=$((total + count))let

total=0
find /home -type f -name "*.txt" | while read -r FILE; do
    total=...
done
echo TOTAL LINES COUNTED:  $total

그게 무슨 뜻인지는 말하지 않았지만 문제 중 하나는 Bash에서 파이프라인의 일부가 기본적으로 하위 셸에서 실행되므로 루프 total내부에서 변경된 내용이 while루프 후에 표시되지 않는다는 것입니다. 바라보다:내 변수가 하나의 "읽는 동안" 루프에서는 로컬이지만 겉보기에 유사한 다른 루프에서는 로컬이 아닌 이유는 무엇입니까?

다음을 사용하거나 다음을 사용 shopt -s lastpipe하여 셸에서 파이프라인의 마지막 부분을 실행할 수 있습니다 .whileecho

find ... | { while ...
    done; echo "$total"; }

물론 find ... | while read -r FILE;줄 바꿈을 포함하거나 공백으로 시작/끝나는 파일 이름에는 문제가 있습니다. 다음 방법으로 이 문제를 해결할 수 있습니다.

find ... -print0 | while IFS= read -r -d '' FILE; do ...

또는 파일당 줄 수의 분석에 신경 쓰지 않고 파일이 마지막 줄 바꿈을 잃지 않고 완전한 텍스트 파일이라는 것을 알고 있다면 간단히 모두 연결하고 wc -l해당 .

파일의 마지막 줄 끝에 개행 문자가 누락되어 있고 마지막 불완전한 줄을 계산하려는 경우에는 그렇게 할 수 없으며 grep -c ^대신 를 계속 사용해야 합니다 wc -l(마지막 부분 줄을 계산하는 것은 거의 . grep -c ^이유 대신 사용할 수 있는 유일한 방법입니다 wc -l.)

바라보다:파일 끝에 새 줄을 추가하는 이유는 무엇입니까?그리고텍스트 파일이 줄바꿈으로 끝나야 하는 이유는 무엇입니까?그게 다야.

또는 총계만 원하고 패턴과 일치하는 모든 파일이 일반 파일이고( -type f테스트를 제거할 수 있음) Bash 및 GNU grep이 있는 경우 다음을 수행할 수도 있습니다.

shopt -s globstar
shopt -s dotglob
grep -h -c ^ **/*.txt | awk '{ a += $0 } END { print a }'

**/*.txt재귀적 글로브이며 작동하려면 명시적으로 활성화해야 합니다. dotglob이 glob이 점으로 시작하는 파일 이름과도 일치하게 합니다. grep -h출력의 파일 이름은 억제되고 awk스크립트는 합계를 계산합니다. 파일 이름은 인쇄되지 않으므로 일부 파일에 버그가 있더라도 작동합니다.

또는 @fra-san이 제안한 것처럼 현재 삭제된 다른 답변을 기반으로 합니다.

grep -r -c -h --include='*.sh' ^ |awk '{ a+= $0 } END {print a }'

답변3

let total+=count작동하므로 $(( ))이 형식에 대한 산술 평가가 필요하지 않습니다.

하지만 이 작업을 수행하는 데에는 이를 사용하는 것이 더 좋습니다 wc -l.

find /home -type f -name '*.txt' -exec wc -l {} +

위의 쉘 스크립트와 같이 출력을 사용자 정의하려는 경우 또는 파일 이름 수가 Linux에서 bash의 ~2MB 행 길이 제한을 초과할 수 있는 경우 awk또는를 사용하여 perl계산할 수 있습니다. 쉘을 읽는 동안 루프를 사용하는 것보다 더 나은 것은 무엇이든 있습니다(참조쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?). 예를 들어:

find /home -type f -name '*.txt' -exec perl -lne '
  $files{$ARGV}++;

  END {
    foreach (sort keys %files) {
      printf "%s has %s lines\n", $_, $files{$_};
      $total+=$files{$_}
    };
    printf "TOTAL LINES COUNTED: %s\n", $total
  }' {} +

참고: find ... -exec perl위 명령은 빈 파일을 무시하지만 이 wc -l버전에서는 해당 파일을 줄 번호 0으로 나열합니다. Perl도 동일한 작업을 수행하도록 만들 수 있습니다(아래 참조).

OTOH, 행 개수와 합계를 계산합니다.어느하나의 쉘 명령줄에 모두 맞지 않더라도 파일 수 - 버전이 wc -l인쇄됩니다.이 경우에는 더 많은 total행이 발생합니다. 아마도 발생하지 않을 수도 있지만 발생하더라도 원하는 것은 아닙니다.

이것은 작동해야 하며 출력을 사용 wc -l하고 Perl로 파이프하여 원하는 출력 형식으로 변경합니다.

$ find /home -type f -name '*.txt' -exec wc -l {} + |
    perl -lne 'next if m/^\s+\d+\s+total$/;
               s/\s+(\d+)\s+(.*)/$2 has $1 lines/;
               print;
               $total += $1;

               END { print "TOTAL LINES COUNTED:  $total"}'

답변4

이 시도:

#!/bin/bash
export total=$(find . -name '*.txt' -exec wc -l "{}" ";" | awk 'BEGIN{sum=0} {sum+=$1} END{print sum}')
echo TOTAL LINES COUNTED ${total}

관련 정보