如何計算所有.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工具可以從 nul 分隔流中讀取路徑名。您可以在這些系統上使用它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 中,管道的各個部分默認在子 shell 中運行,因此循環total內部所做的任何更改while在循環之後都不可見。看:為什麼我的變數在一個「while read」迴圈中是本地變量,但在另一個看似相似的循環中卻不是?

您可以使用shopt -s lastpipe讓管道的最後一部分在 shell 中運作;或將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是一個遞歸 glob,需要明確啟用它才能運作。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 {} +

如果您想要像上面的 shell 腳本一樣自訂輸出,或者如果檔案名稱的數量可能超出 linux 上 bash 的 ~2MB 行長度限制,您可以使用awkperl來進行計數。任何東西都比 shell while-read 循環更好(參見為什麼使用 shell 循環處理文字被認為是不好的做法?)。例如:

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 列出它們。

OTOH,它將進行行計數和總計任何檔案數量,即使它們不能全部容納在一個 shell 命令行中 - 版本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}

相關內容