我有一个使用 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; }