我有一個使用 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
,則在下一個循環中,將列印「前綴」。
當您連接兩個序列時,可以測試整個事情:
{
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; }