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; }