Perl 的反引号/系统给出“tcsetattr:输入/输出错误”
Posted
技术标签:
【中文标题】Perl 的反引号/系统给出“tcsetattr:输入/输出错误”【英文标题】:Perl's backticks/system giving "tcsetattr: Input/output error" 【发布时间】:2011-05-13 20:27:03 【问题描述】:我正在编写一个 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
运行时会出现相同的结果(无论如何,反引号是首选,因为我想要脚本的输出)。当我在命令行上的反引号之间运行确切的命令时,它完全按预期工作。这是什么原因造成的?
【问题讨论】:
【参考方案1】:tcsetattr: Input/output error
消息来自 ssh,当它试图将本地终端置于“原始”模式时(这涉及到对 tcsetattr 的调用;参见 enter_raw_mode
在sshtty.c
中,从client_loop
在clientloop.c
中调用)。
根据 IEEE Std 1003.1, 2004 (Posix) Section 11.1.4: Terminal Access Control,如果调用进程处于孤立 (还是背景?)进程组。
有效地 ssh 正在尝试更改本地终端的设置,即使它不在前台进程组中(由于您的 fork 并且本地脚本正在退出(正如您引用的输出中的错误消息之前的明显 shell 提示所证明的那样)。
如果您只是想避免错误消息,可以使用ssh -ntt
(从/dev/null 重定向标准输入,但无论如何要求远程端分配一个tty)而不是ssh -t
(添加您的-l
当然,还有您需要的任何其他选项。
更有可能的是,只要某些远程进程仍在运行,您就会对保持本地脚本运行感兴趣。为此,您需要使用wait
函数(或其“亲戚”之一)等待每个分叉的进程退出,然后再退出分叉它们的程序(这将使它们保持在前台进程组中,只要启动它们的程序就在其中)。不过,您可能仍想使用-n
,因为如果您分叉的多个 ssh 实例都尝试使用(读取或更改其设置)本地终端同一时间。
作为一个简单的演示,您可以让本地脚本在分叉所有子节点后执行sleep 30
,以便 ssh 命令有时间在它们属于前台进程组。这应该会抑制错误消息,但不会解决您的既定目标。为此,您需要 wait
(如果我正确解释了您的目标)。
【讨论】:
我会在星期一进来的时候试试你的建议。等待:远程脚本永远运行,直到当这个脚本被杀死时它们被杀死,所以我认为我不需要等待它们。不过我得调查一下。【参考方案2】:这可能是因为您在 stdin/stdout 不是真正的 tty 时强制 SSH 分配一个 tty。 SSH 尝试在这些处理程序上调用某些特定的 tty 函数(可能从远程端转发)并且调用失败并返回一些错误。
你有什么理由应该分配一个 tty 吗?
还有什么理由使用过时的 SSH 协议版本 1?
【讨论】:
我认为这是一个 ell,即登录选项,而不是一个。 分配一个 tty 因为没有它,当这个脚本被杀死时,远程进程继续运行。当这个人被杀死时,我需要他们被杀死,而 -t 是我知道的唯一方法。以上是关于Perl 的反引号/系统给出“tcsetattr:输入/输出错误”的主要内容,如果未能解决你的问题,请参考以下文章