【问题标题】:SSH - terminal misbehaving when SSH invoked from a `while read` loopSSH - 从“while read”循环调用 SSH 时终端行为异常
【发布时间】:2018-05-01 01:42:43
【问题描述】:

我正在编写一个 Bash 脚本来跨多个服务器自动执行任务。

我正在通过 SSH 登录到 Centos 7 机器以运行一些编辑器(nanovi、...)

ssh -tt centos@... '/bb/Conf edit'

/bb/Conf edit 基本上就是vi /bb/conf.yaml

当我从我的 shell 运行 SSH 命令时,它运行良好。但是,当从 while read ...; do 循环内的 Bash 脚本运行相同的 SSH 命令时,编辑器的大小错误(我猜是 80x40)并且似乎忽略了我按下的键 - 即在 nano 中,Ctrl+x 没有不做任何事。唯一有效的键是 Ctrl+c 关闭连接。

我认为这与TERM 变量有关,根据this,所以我尝试将export TERM=xtermTERM=rxvt 添加到/bb/Conf 或调用SSH 的地方。该变量实际上是在目标环境中设置的(我在vi 之前尝试过echo $TERM)。但是终端仍然行为不端。

然后我尝试将单个命令 ssh ... 放到一个新脚本中。运行时,编辑器运行良好。

过了一会儿,我发现它在while read 循环之外有效,但在循环内部无效。我假设编辑器做了一些 stdin/stdout 魔术,然后 read 以某种方式打破了它。

有没有办法在循环中运行像 vinano 这样的编辑器?

(我的目的是允许用户在多个服务器上编辑文件。)

【问题讨论】:

  • 事实证明,它以某种方式被while read HOST_; do 破坏了,ssh 在里面。我会重写问题。

标签: bash ssh terminal console


【解决方案1】:

这是因为readssh 都从同一个输入流中读取。解决方案是为 while 读取循环使用不同的文件描述符:

while IFS= read -r -u3 line; do
    ssh ...
done 3< file

在这里,我们使用文件描述符 3 而不是标准输入。


冗长的管道可能难以阅读和维护,但您可以建设性地使用空格:在|&amp;&amp;|| 之后允许换行。此外,括号引入了一个包含任意脚本的子shell,因此缩进会有所帮助。

while read -u3 line; do
    : do stuff here that needs to read from stdin
done 3< <(
    command 1 of the pipeline |
    command 2 |
    command 3
)

这是干净和可读的。缺点是它将管道的最后一部分(while 循环)放在首位,因此代码类型会向后流动。

【讨论】:

  • 如果循环正在读取命令(或管道)的输出,您可以使用done 3&lt; &lt;(somecmdorpipeline) 而不是常规管道。请注意,这是一个仅限 bash 的功能,因此请在脚本中使用 bash shebang,并且不要通过使用 sh 命令运行它来覆盖它。
  • 是的,它来自一个有点长的管道。我认为没有办法使用| 从左到右的语法?
  • 也许我可以使用其他循环?它使用的行只是主机名。
  • IFS= 是干什么用的?我的意思是,我知道它设置了分隔符,但它在这里做了什么?
  • 这就是从文件中准确读取一行的方法。如果您不使用IFS=,则任何前导/尾随空格都将被忽略。 IFS= read -r line 仅在读取命令期间将 IFS 变量设置为空字符串。 -r 选项指示 read 将反斜杠作为普通字符而不是转义序列的开头来处理。
猜你喜欢
  • 2019-08-13
  • 2013-02-14
  • 1970-01-01
  • 2020-02-15
  • 2017-09-09
  • 2012-07-13
  • 2020-04-18
  • 1970-01-01
相关资源
最近更新 更多