Hub*_*bro 5 bash sed sh shell-script
我有一个使用 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: /"
Run Code Online (Sandbox Code Playgroud)
问题是,正如我所解释的,没有部分行打印到屏幕上。如果我移除| sed零件,它会按预期工作。首先是这样写的:
正在导入新数据...
几秒钟后,该行完成:
正在导入新数据...完成
我假设sed只能逐行工作。我尝试将其设置为无缓冲,但它仍然等待整行。有没有另一种方法来实现这一点?
这有点棘手,因为所有这些实用程序(sed、awk、grep)都是行缓冲的。这意味着它们仅在该行完成(出现换行符)时才打印输出。他们无法逐字符读取输入。
因此,为了进行测试,我制作了一个小序列,模拟您的行为:
{
echo -n "first task: "
sleep 2
echo "done"
echo -n "second task: "
sleep 2
echo "done"
}
Run Code Online (Sandbox Code Playgroud)
正如您的问题一样,它会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; }
Run Code Online (Sandbox Code Playgroud)
解释:
内置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; }
Run Code Online (Sandbox Code Playgroud)