Perl的反引号/系统给出“tcsetattr:输入/输出错误”

时间:2011-05-13 20:27:03

标签: perl ssh

我正在编写一个perl脚本,它使用ssh在几个不同的服务器上启动脚本。只要此脚本正在运行,远程脚本就需要运行:

#!/usr/bin/perl
require 'config.cfg'

#@servers is defined in config.cfg
#Contains server info as [username,hostname]
#
# @servers = ([username,server1.test.com],[username,server2.test.com]) 
#
foreach $server ( @servers ) {
    my $pid = fork();
    if ( $pid == 0 ) {
        $u = ${$server}[0];
        $h = ${$server}[1];
        print "Running script on $h \n";
        print `ssh -tl $u $h perl /opt/scripts/somescript.pl`;
        exit 0;
    } else {
        die "Couldn't start the process: $!\n";
    }
}
[...]

当我运行此脚本时,我得到以下输出:

  

./ brokenscript.pl
  在server01上运行脚本
  $ tcsetattr:输入/输出错误
  与server01的连接已关闭。

使用system运行时会出现相同的结果(因为我想要脚本的输出,所以反正是首选的)。当我在命令行上的反引号之间运行确切的命令时,它完全按预期工作。造成这种情况的原因是什么?

2 个答案:

答案 0 :(得分:4)

tcsetattr: Input/output error消息来自 ssh ,当它尝试将本地终端置于“原始”模式时(涉及调用 tcsetattr ;请参阅{ enter_raw_mode中的{1}},来自sshtty.c中的client_loop

如果调用过程是,则从IEEE Std 1003.1,2004(Posix)Section 11.1.4: Terminal Access Control tcsetattr 可能会返回带有clientloop.c的-1(即“输入/输出错误”)在孤儿(或背景?)过程组中。

有效 ssh 正在尝试更改本地终端的设置,即使它不在前台进程组中(由于您的 fork 而且本地脚本已退出) (由引​​用输出中错误消息之前的明显shell提示证明)。

如果您只是想避免错误消息,可以使用errno == EIO(从/ dev / null重定向stdin,但要求远程端分配tty)而不是ssh -ntt(添加你需要的ssh -t和其他任何选项。当然。)


更有可能的是,只要某些远程进程仍在运行,您就可以保持本地脚本的运行。为此,您需要使用wait函数(或其中一个“亲戚”)等待每个分叉进程退出,然后退出分叉它们的程序(这将使它们保留在前台进程组中只要启动它们的程序在其中)。您可能仍然希望使用-l,因为如果您分叉的 ssh 的多个实例都试图使用(从本地读取或更改设置),那将会造成混淆终端在同一时间。

作为一个简单的演示,你可以让所有孩子离开后让本地脚本做-n,这样 ssh 命令就有时间启动了前台进程组的。这应该可以抑制错误消息,但不会解决您的既定目标。你需要sleep 30(如果我正确地解释你的目标)。

答案 1 :(得分:0)

这可能是因为当stdin / stdout不是真正的ttys时,你强迫SSH分配tty。 SSH尝试在这些处理程序上调用某些特定的tty函数(可能从远程端转发),并且调用无法返回一些错误。

你有什么理由要分配tty吗?

是否还有理由使用SSH协议的过时版本1?