行全体を待たずにプログラム出力の先頭に文字列を追加するにはどうすればよいでしょうか?

行全体を待たずにプログラム出力の先頭に文字列を追加するにはどうすればよいでしょうか?

SSH を使用してリモート サーバーでコマンドを実行するスクリプトがあります。Remote:出力の各行の先頭に文字列を追加したいのですが、行全体が利用可能になるまで各行を遅延させたくありません。これが私のコマンドの出力です。

$ myproject-db-push my_database_name
データベースからエクスポートしています...完了
データをアーカイブしています...完了
アーカイブをリモートにアップロードしています...完了
リモートでインストールスクリプトを実行中
リモート: アーカイブを一時ディレクトリに解凍しています...完了
リモート: 使用データベース: my_database_name
リモート: コレクションを削除しています:
リモート: - my_collection_foo
リモート: - my_collection_bar
リモート: 新しいデータをインポートしています...完了

この場合、sed次のように使用します:

echo "$INSTALLCMD" | ssh -T "deploy@$SERVER" | sed -u "s/^/Remote: /"

問題は、私が説明したように、部分的画面に行が出力されます。この| sed部分を削除すると、期待どおりに動作します。まず、次のように記述します。

新しいデータをインポートしています...

そして数秒後、行が完成します。

新しいデータをインポートしています...完了

sed行単位でしか動作できないと想定しています。バッファなしに設定してみましたが、それでも行全体を待機します。これを実現する別の方法はありますか?

答え1

sedこれらすべてのユーティリティ ( 、awk、 ) は行バッファリングされるため、少し扱いに​​くいですgrep。つまり、行が終了したとき (改行が現れたとき) にのみ出力が印刷されます。入力を文字ごとに読み取ることはできません。

そこでテストのために、あなたの動作をシミュレートする小さなシーケンスを作成しました。

{ 
  echo -n "first task: "
  sleep 2
  echo "done"
  echo -n "second task: "
  sleep 2
  echo "done"
}

質問にあるように、first task:2 秒後に とが出力されますdone。ターミナルにコピーして自分で試してみてください。

解決:

コマンドの後ろに以下を追加します。

IFS=
command | { x=1; while IFS= read -d'' -s -N 1 char; do
  [ $x ] && printf "Remote: "
  printf "$char"
  unset x
  [ "$char" == "
" ] && x=1
done; }

説明:

組み込みreadの は、bash入力を文字ごとに読み取ることができます。 の部分はread -d'' -s -N 1 char区切り文字を無効にし-d''、サイレント モードを有効にして-s、一度に 1 文字だけを-N 1変数 に読み込みます$char。次に、コマンドは変数が$x存在するかどうかを確認します。存在する場合は、新しい行に入り、「プレフィックス」を出力します。次に、文字を出力します。 を設定解除します$x。次に、最後のステートメントで、文字が改行かどうかを確認します。改行の場合は$xに設定され1、次のループで「プレフィックス」が出力されます。

2 つのシーケンスを連結すると、全体をテストできます。

{ 
  echo -n "first task: "
  sleep 2
  echo "done"
  echo -n "second task: "
  sleep 2
  echo "done"
} | { x=1; while IFS= read -d'' -s -N 1 char; do
  [ $x ] && printf "Remote: "
  printf "$char"
  unset x
  [ "$char" == "
" ] && x=1
done; }

関連情報