web-dev-qa-db-ja.com

行を受け取るとカウントを表示する `wc -l`代替

wc -lを使用して、コマンドの出力の行をカウントしています。これは、入力がパイプされているためです。

commad | wc -l

これは正常に機能しますが、commandが重い計算を行っている場合、これは非常に遅くなります。 「これまでにパイプされた」行の数を表示する代替はありますか?

このようなことは、次のようなアイテムごとの計算を行うときに特に役立ちます。

cat something | xargs -L1 heavy-per-line-computation | wc -l

これを手動で行う方法の1つは、出力をファイル(command > file)にパイプし、定期的にcat file | wc -lを実行することです。しかし、1つのコマンド(無駄なI/Oを回避するためにファイルにリダイレクトされません)が目的です。

2
awk '{print NR}'

このコマンドは、検出された行ごとに新しい番号を出力します。最後の行が完了すると、最後の番号がwc -lの発言と一致します。最終行が incomplete の場合、awkはそれを数える可能性があります(私のKubuntuではGNU awkはそうです)が、wc -lは(実際には改行を数えるため)しないので、不一致がある可能性があります。

もう1つの相違点は、入力が完全に空の場合です。wc -l0を出力し、awkは何も出力しません。印刷するには0を使用します:

awk '{print NR} END {if (NR==0) print NR}'

または、コンソールの同じ行にある古い番号を新しい番号ごとに上書きしたい場合があります。次にこれ:

awk '{printf "\r%s",NR} END {print "\r"NR}'

例:yes | head -n 76543 | awk '{printf "\r%s",NR} END {print "\r"NR}'

コマンドが入力を消費することに注意してください(teeが便利な場合があります)。監視目的のために、あなたは興味があるかもしれません:

awk '{print NR OFS $0}'

これは(デフォルトのOFSはスペースです)はcat -nとほぼ同じです(cat-nをサポートしている場合)。


pv -lは行をカウントし、パイプライン内で使用できます。例:

for i in 1 2 3 4 5; do date; sleep 1; done | pv -l | wc -l

出力を最小限に抑えるには、pv -lbを検討してください。

6

Rubyを使用したソリューションを次に示します。

count_linesは(stderrを介して)最大0.5秒ごとに、これまでに受信した行の数と、最後の合計数(stdoutを介して)を出力します。

read -d '' make_lines <<'EOF'
  STDOUT.sync = true
  [0.2, 0.1, 0.5, 0.1, 0.6, 0.1, 0.3, 0.1, 0.3, 0.01, 0.01].each do |t|
    puts
    sleep t
  end
EOF

read -d '' count_lines <<'EOF'
  lines = 0
  t = 0
  while gets do
    lines += 1
    now = Time.now.to_f
    if now - t > 0.5
      warn lines
      t = now
    end
  end
  puts lines
EOF

Ruby -e "$make_lines" | Ruby -e "$count_lines"
2
ens

pv(パイプビュー)を探していると思います。

seq 100000000000 | pv -l | wc -l
1
Ole Tange

ほとんどの端末はCR (\r)を「列1に移動」と見なします。これにより、以前の出力を上書きし続けることができます。

これは、標準入力から読み取るスクリプトdwcです(tail -fファイルから、または出力プロセス置換から)。スクリプトにはサンプルテストベッドがあります。

-lオプション。デフォルトでは、入力行番号を端末の現在の行に6桁の数字として出力します。 -l、受信した最後の行の最初の60文字も出力し、以前のテキストをクリアします。 EOFが検出されると、改行が発行されるため、最後の出力の下にプロンプ​​トが表示されます。

#! /bin/bash

#.. dwc [-l]

AWK_SHORT='
BEGIN { Fmt = "\r%6d "; }
{ printf (Fmt, NR); }
END { printf ("\n"); }
'

AWK_LONG='
BEGIN { Fmt = "\r%6d  %.60s"; Clr = sprintf ("%60s", ""); }
{ printf (Fmt, NR, $0 Clr); }
END { printf ("\n"); }
'
    if [[ "${1}" = "-l" ]]; then
        awk "${AWK_LONG}"
    else
        awk "${AWK_SHORT}"
    fi

    exit

    #.. Test method.
    man ls | head -n 40 |
         while IFS='' read X; do
             printf '%s\n' "${X}"; sleep 0.75
         done |
         tee >( ./dwc -l ) > foo.txt
0
Paul_Pedant