Moreutils tsによる配管


9

シリアルポートに着信ストリームがあり、毎秒約1回新しいラインが表示されます

wren@Raven:~$ cat /dev/ttyUSB0

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

空白行を取り除き、残りにタイムスタンプを付けたい。

sedは空白行をカリングしてタイムスタンプを追加しますが、タイムスタンプを更新することはできません。呼び出された時間を報告するだけです。

wren@Raven:~$ cat /dev/ttyUSB0 | sed -e '/^$/d' -e "s/$/`date +\,%F\,%T`/"
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
^C

Moreutilsの一部であるtsを見つけました。これにパイプして更新タイムスタンプを取得できます。

wren@Raven:~$ cat /dev/ttyUSB0 |  ts
May 14 09:49:26 A_Sensor1,B_22.00,C_50.00
May 14 09:49:26
May 14 09:49:27 A_Sensor1,B_22.00,C_50.00
^C

ただし、tsとsedを適切に組み合わせることができません。

これは私が望むことをするように見えるが、出力をまったく生成しない

wren@Raven:~$ cat /dev/ttyUSB0 | sed -e '/^$/d' | ts
^C
wren@Raven:~$

ただし、パイプの順序を逆にしても出力は生成されますが、もちろん空白ではなくなった行は取り除かれません。他の置換は問題なく機能するので、sedへのパイプが機能していることがわかります。

wren@Raven:~$ cat /dev/ttyUSB0 |  ts | sed -e '/^$/d'
May 14 10:07:25 A_Sensor1,B_22.00,C_50.00
May 14 10:07:25
May 14 10:07:26 A_Sensor1,B_22.00,C_50.00
May 14 10:07:26
^C

だから私は少し困惑しています。私はおそらくsedに不要な行を削除させることができますが、削除する前にそれらにタイムスタンプを付けるのは間違ったアプローチでなければなりません。

説明と助けをお願いします。

回答:


9

質問に直接答えることsedは、バッファリングであり、それが唯一の問題です。
これを修正するには、-u/ --unbufferedフラグでバッファリングしないように指示します。

sed -u '/^$/d' /dev/ttyUSB0 | ts

テストハーネスを使用して(ただし、証明のために実行する必要があります):

$ (echo -e 'banana\n\n'; sleep 2; echo 'cheese') | sed -u '/^$/d' | ts
May 14 11:26:05 banana
May 14 11:26:07 cheese

他のストリームエディタで同様の問題に遭遇する可能性があります。彼らは一見すべてを少しバッファリングしたいようです。ただし、すべて回避策があります。以下は、私がテストした一連のコマンドです。

... | mawk -W interactive '/./' | ts
... | gawk '/./ { print $0; fflush(); }' | ts
... | grep --line-buffered '.' | ts
... | perl -n -e 'print if /./' | ts

別のアイデアは、それをgawk処理させるだけです。空でない行をフィルタリングして日付を出力することができます(SOのKieronに感謝します)。

awk '/./ { print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' /dev/ttyUSB0

これは、行が入った直後にフラッシュしgawkます。他のことをしたい場合は、ここで特に役立ちます...出力の4番目の列(pre- ts)が正規表現と一致することを確認したい場合、次のことができます(例:)$4~/\d{4}/。Awk(およびそのバリアント)は、ストリーム処理に対して非常に柔軟です。

別のテストハーネス:

$ gawk '/./ { print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' <(
      echo -e 'banana\n\n';
      sleep 2;
      echo 'cheese'
  )
2014-05-14 11:13:59 banana
2014-05-14 11:14:01 cheese

1
の+1 sed -u。これは、ブロックバッファリングとラインバッファリングの問題です。
jfs 2014年

@Oli sed -uは、tsにパイプしたときにも完全に機能するため、バッファリングについて説明します。困惑することはもうありません。
2014年

awkは特にこのようなことに適しています。awkコードは通常、sedよりも密度が低く、読みやすく、デバッグ中に部分的な結果を表示したいだけ多くの印刷ステートメントを投入できます。別のファイルを使用しないようにawkプログラム全体をhereドキュメントに挿入できます。また、hereドキュメントの終了文字列を引用符で囲むと、bashは通常解釈しようとする埋め込みトークンをすべて無視します。
ジョー

0

bashはこれをwhile readループで処理できます

(echo -e 'banana\n\n'; sleep 2; echo 'cheese') | 
while IFS= read -r line; do 
    [[ $line ]] && echo "$(date "+%F %T") line"
done
2014-05-14 06:34:06 banana
2014-05-14 06:34:08 cheese

トリッキーなパラメーター展開により、空白のみの行を削除できます。先頭の空白をすべて削除し、行が空かどうかを確認します。

shopt -s extglob

(echo -e '  banana\n\t\n'; sleep 2; echo 'cheese') |
while IFS= read -r line; do
    [[ "${line/#+([[:blank:]])/}" ]] && echo "$(date "+%F %T") $line"
done

そのような様々なアプローチを試しましたが、どれもうまくいきませんでした。私もあなたのコードを動作させることができません。echoまたはcatを使用して/ dev / ttyUSB0をwhileループに送信すると、出力は1行になります:2014-05-14 12:23:32行
当惑

もっと良い方法があると確信していますがtail -f /dev/ttyUSB0、猫やエコーの代わりに試してください。実行し続けます。私のシステムでこれをテストする方法がわかりませんでした。
ジョー

tail -f / dev / ttyUSB0は、whileループの有無にかかわらず、出力を提供しません。あなたのコメントのためのtvm。
困惑
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.