如何在不等待整行的情况下将字符串添加到程序输出中?

如何在不等待整行的情况下将字符串添加到程序输出中?

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

这有点棘手,因为所有这些实用程序(sedawkgrep)都是行缓冲的。这意味着它们仅在行结束时(出现换行符)才打印输出。它们无法逐个字符地读取输入。

因此,为了测试,我制作了一个小序列,模拟你的行为:

{ 
  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。然后最后一条语句检查字符是否为换行符。如果是换行符,则设置为,$x1在下一个循环中打印“前缀”。

当你将两个序列连接起来时,就可以测试整个过程:

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

相关内容