「このログ、結局なん件エラーが出てるんだろう」と、画面をスクロールしながら目で数えたことはないでしょうか。
行や文字を数えるのが wc、出力を画面に流しつつファイルにも残すのが tee です。
どちらも単体では地味なコマンドですが、パイプの端や途中に置いた途端に効いてきます。
この記事では、**数える wc と、流れを分ける tee** の最小限を、手元で実際に動かした結果と一緒にまとめました。
wc の基本 ——行・単語・バイトを数える
wc は引数にファイルを渡すだけで、3つの数字を並べて返します。
$ printf 'a\nb\nc\n' > f1.txt
$ wc f1.txt
3 3 6 f1.txt
左から 行数・単語数・バイト数 の順で、最後にファイル名が付く形です。
必要な数字だけほしいときはオプションで絞るのが基本。
行数なら -l、単語数なら -w、バイト数なら -c を付けます。
wc -l app.log # 行数だけ
wc -w report.txt # 単語数だけ
wc -c image.png # バイト数だけ
日本語を扱うときに効いてくるのが、-c と -m の違いです。
-c はバイト数、**-m は文字数** なので、マルチバイト文字では値がずれます。
$ printf 'あいう\n' > f3.txt
$ wc -c f3.txt
10 f3.txt
$ wc -m f3.txt
4 f3.txt
UTF-8 の「あいう」は3文字で9バイト、そこに改行が1つ加わって 10 バイト・4 文字という結果になりました。
複数のファイルをまとめて渡すと、末尾に total の行が付きます。
$ wc -l f1.txt f2.txt
3 f1.txt
1 f2.txt
4 total
wc でつまずきやすいところ
wc -l は行を数えているように見えて、実際は **改行文字の個数** を数えています。
そのため末尾に改行のないファイルでは、最後の行が数に入りません。
$ printf 'x\ny' > f2.txt # 末尾に改行なし
$ wc -l f2.txt
1 f2.txt
見た目は2行なのに 1 と返ってくるので、件数をそのまま判定に使うときは気をつけたいところ。
もうひとつ、出力からファイル名を消したいときの小技があります。
引数で渡す代わりにリダイレクトで流し込むと、返ってくるのは数字だけです。
$ wc -l f1.txt
3 f1.txt
$ wc -l < f1.txt
3
シェル変数に入れて計算に使うなら、こちらの形のほうが扱いやすいです。
なお、grep の結果を数えるだけなら grep -c のほうが素直。
$ printf 'ERROR a\nok\nERROR b\n' > f4.txt
$ grep ERROR f4.txt | wc -l
2
$ grep -c ERROR f4.txt
2
同じ 2 が返るなら、パイプが1本減るぶん後者のほうが読みやすくなります。
tee の基本 ——流しながら保存する
tee はパイプで受け取った内容を、**画面に流しつつ同時にファイルへも書き込む** コマンドです。
$ printf 'l1\nl2\n' | tee run.log
l1
l2
$ cat run.log
l1
l2
名前の由来は配管のT字管と言われていて、流れを2つに分ける形そのままの動きをします。
既存の内容を残したまま追記したいなら -a です。
$ echo "l3" | tee -a run.log
l3
$ cat run.log
l1
l2
l3
リダイレクトの > と >> の関係と同じで、-a が追記にあたると覚えておけば迷いません。
tee の定番の使いどころ
いちばん効くのが、sudo と組み合わせて権限のあるファイルへ書く場面です。
リダイレクトを実行するのは sudo ではなく手元のシェルなので、素直に書くと権限不足で弾かれます。
あいだに tee を挟めば、書き込む主体そのものが sudo 側へ移るという理屈です。
# リダイレクトはシェルが実行するので権限不足になる
sudo echo "127.0.0.1 example.local" >> /etc/hosts
# tee なら sudo が書き込むので通る
echo "127.0.0.1 example.local" | sudo tee -a /etc/hosts
標準エラーもまとめてログに残したいときは、2>&1 を先に付けてから tee へ渡します。
./build.sh 2>&1 | tee build.log
そしてもうひとつ、忘れると地味に痛い落とし穴。
cmd | tee log の終了ステータスは **tee のもの** になるので、cmd が失敗しても 0 が返ってきてしまいます。
$ false | tee /dev/null; echo "exit=$?"
exit=0
$ (set -o pipefail; false | tee /dev/null; echo "exit=$?")
exit=1
CI やシェルスクリプトで tee を挟むなら、set -o pipefail を入れておくと失敗をちゃんと拾えます。
bash なら ${PIPESTATUS[0]} で左側の終了コードを直接読む手もあるので、書きやすいほうを選んでください。
最後に
数えるなら wc、流しながら残すなら tee。
どちらも短いコマンドですが、パイプの端に足すだけで作業がぐっと楽になります。
特に 2>&1 | tee build.log は、ビルドやデプロイのログを残す型として覚えておくと長く使えるはず。
リダイレクトそのものの仕組みは、別記事にまとめてあります。
以上です。









コメントを残す