USBでMacに繋ぐとシリアルデバイスとして認識される温度湿度気圧センサがあるとします。
このセンサは1秒に1回 20.00,10.00,1000.00
という形式のデータを送ってきます。
20.00
が温度、10.00
が湿度、1000.00
が気圧です。
手元にセンサがない人もいることでしょうし、センサのかわりに以下のようなスクリプトを用意します。
#!/bin/sh
while :; do
echo 20.00,10.00,1000.00
sleep 1
done
これを実行すると、以下のように1秒ごとにデータが流れてきます。
$ ./sensor.sh
20.00,10.00,1000.00
20.00,10.00,1000.00
20.00,10.00,1000.00
20.00,10.00,1000.00
20.00,10.00,1000.00
.
.
.
温度だけ取り出したいので、cutコマンドを使ってみます。
./sensor.sh | cut -d, -f1
20.00
20.00
20.00
.
.
.
うまくいきました。
今度は温度をコンソール上で確認しながら同時にファイルに記録しておきたいので、tee
に渡してみます。
$ ./sensor.sh | cut -d, -f1 | tee temp
あれれ、なにも出てきません。temp
の中身も空っぽです。
どうやら、端末以外に出力するときは、cut
が標準出力をバッファリングするみたいです。
cut buffering
などでGoogleで検索すると、stdbuf
というコマンドを使うとバッファリングなしを強制できるようです。
ところがstdbuf
はGNUとBSDでともに提供されているものの、macOSにははいっていません。これはちょっとめんどくさいです。
いろいろ悩んだ挙句、while
とread
を使えばPOSIXの範疇で簡単に解決できることに気づきました。
$ ./sensor.sh | while read l; do echo $l | cut -d, -f1; done | tee temp
20.00
20.00
20.00
20.00
20.00
^C
$ cat temp
20.00
20.00
20.00
20.00
20.00
1秒ごとに20.00
が表示されます。うまくいきました。
ワンライナーだと見づらいのでシェルスクリプトとして書いてみます。
./sensor.sh |
cut -d, -f1 |
while read line; do
echo ${line} |
cut -d',' -f1
done |
tee temp
タイトルに反して「バッファリングさせないようにしている」わけではないですが、ラインバッファを強制したときと同じ結果が得られました。
cut
を毎回起動するのでパフォーマンスの面では劣りますが、POSIXの範囲内で簡単に解決できたのと、元々多くても1秒間隔くらいでやってくるデータを想定しているので、これでじゅうぶんだと思います。
cut
に限らず、awk
など出力をバッファリングしてしまうコマンドに出くわしたときに使える手だと思います。