모든 .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
하여 셸에서 파이프라인의 마지막 부분을 실행할 수 있습니다 .while
echo
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}