web-dev-qa-db-ja.com

行を読んでいる間にbashで合計する

フォルダー内の.jsファイルの行の合計を考え出そうとしています。私はこれをbashで使用しています:

sum=0 && find . | grep ".js" | while read -r f; do wc -l $f | awk '{print $1;}'; done;

awk内に$sum += $1を配置しても機能しません。これをどうやって行うのですか?

PS:私はこれを使用してはるかに簡単に達成できることを知っています

find . -name '*.js' | xargs wc -l

私はまだ上記の解決策を求めています。

3
d9ngle

この簡単で超高速なソリューションを試してください:

find . -type f -name "*.js" -exec cat {} + | wc -l

以前にwcを使用していくつかの解決策を試しましたが、それらには問題があります。ファイル名の改行や遅い。

11
pLumo

lastpipe Shellオプションを有効にしない限り、bashはパイプラインの各コマンドを個別のサブシェルで実行します

# bash requires job control to be disabled for lastpipe setting
set +m
shopt -s lastpipe

declare -i sum=0
find . -name '*.js' -print0 | while IFS= read -d '' -r name; do
    (( sum += $(wc -l < "$name") ))   # redirect the file into wc for easier output
done
echo $sum

プロセスの置換 は、このサブシェルの問題を処理するのに便利です。

declare -i sum=0
while IFS= read -d '' -r name; do
    (( sum += $(wc -l < "$name") ))   # redirect the file into wc for easier output
done < <(
    find . -name '*.js' -print0
)
echo $sum

ただし、これによりプログラムフローが読みにくくなります。

5
glenn jackman

awkに加算して結果を表示しますか?

awk '{sum +=$1} END {print sum}'トリックを行う必要があります。

私のライブラリでは、bashスクリプトを次のようにしています。

$ find . -type f -name '*.bash' \
| while read -r f ; do wc -l "$f" ; done \
| awk '{sum +=$1} END {print sum}'

結果を取得します522

5
markgraf

概要

ディレクトリの行を数えるには:

shopt -s globstar;                      # valid for bash
set -- ./**/*".js"; cat "$@" | wc -l    # for files under `./` directory

While読み取りループの外側で合計するには

shopt -s globstar;                                 # valid for bash
set -- ./**/*".js"                                 # for files under `./` directory
wc -l "$@" | awk '{sum+=$1} END {print sum-=$1}'   # calculate the sum in awk

しかし、wc -lが合計を最終行に出力する場合、なぜ合計を再計算するのでしょうか。 :

wc -l "$@" | tail -n 1


細部

改善される可能性のある要素がいくつかあります。

  • | awk '{print $1;}'の代わりにwc -l <"$f"を実行する場合、最初のフィールドのみを選択するwc -l $fの部分は必要ありません。単純なリダイレクト(<)により、wcは標準入力でファイルを受信し、印刷するファイル名がなくなります。これにより、スクリプトが次のように削減されます。

    見つける。 | grep ".js" |読み取り中-r f; do wc -l <​​"$ f";できた

  • Findが選択を行う場合、grepを呼び出す必要はありません。

    見つける。 -name '* .js' |読み取り中-r f; do wc -l <​​"$ f";できた

  • 読み取りは、ファイル名から先頭と末尾の空白を削除します。

  • そして実際には、findは各ファイルに対してコマンドを実行できます(暗黙のループ)。

    見つける。 -name '* .js' -exec sh -c 'wc -l <​​"$ 1"' foo '{}' \;

  • また、ファイルごとに1つではなく、wcを1回グローバルに呼び出すことも可能です。

    見つける。 -name '* .js' -exec sh -c 'cat "$ @" | wc -l 'foo' {} '+

ただし、スペース、タブ、改行、またはグロブ文字(*、?、[)の問題なしに各ファイル名を処理するためにシェルを再度呼び出す必要があることは、特別な検索が必要ない場合は、シェルで直接解決できることを示しています。リンクの解決。

set -- *.js; cat "$@" | wc -l # for the present directory

または

shopt -s globstar;                      # valid for bash
set -- ./**/*".js"; cat "$@" | wc -l    # for files under `./` directory

読み取りループの外側で合計

タイトルの質問はあなたのパイプのこの部分に関するものです:

while read -r f; do wc -l $f …

上記のように、ファイルのリストが引数リスト($@)(または配列内にある場合もあります)にあるとすると、最初のフィールドとして行数を含むファイルのリストが出力されます。

$ printf '%s\n' "$@" | while read -r f; do wc -l "$f"; done
12 filea.js
21 fileb.js    

この時点で、awkを使用して新しいパイプを追加し、最初のフィールドを選択できます。

$ printf '%s\n' "$@" | while read -r f; do wc -l "$f"; done | awk '{print $1}'
12
21

ただし、+を追加して、すべてを1行で出力することもできます。

$ printf '%s\n' "$@" | 
> while read -r f; do wc -l "$f"; done | 
> awk '{printf( "%s+",$1)}'
12+21+

そして、末尾に0を追加し、bcですべてを合計します。

$ printf '%s\n' "$@" | 
> while read -r f; do wc -l "$f"; done |
> awk '{printf("%s+",$1)}END{print 0}' |
> bc
33

ただし、すでに述べたように、wc -l <"$f"`` and you can convert the newlines to + , then add a0`を使用したファイル名の出力を回避し、bcに計算を行わせることができます。

$ printf '%s\n' "$@" |
  while read -r f; do wc -l <"$f"; done |
  { tr '\n' '+'; echo 0; } |
  bc

33

またはawkに合計を計算させる:

$ printf '%s\n' "$@" | 
  while read -r f; do wc -l <"$f"; done | 
  awk '{sum+=$1} END {print sum}'

33
1
Isaac

これを行う別のbash方法を次に示します。

sum=0 && while IFS= read -r -d '' f; do let sum+=$(sed -n "\$=" "${f}"); done < <(find . -name '*.js' -print0 2>/dev/random) && echo "$sum"

find ... -print0 -- prints with null terminated 
            IFS= -- deset IFS 
   read -r -d '' -- -r do not allow backslash escapes, -d '' set delimiter to NULL
   let sum+=(..) -- add up sum of sed $= "$f", $: last line, =: line #
0
A.Danischewski