Perl 的反引号/系统给出“tcsetattr:输入/输出错误”
我正在编写一个 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
运行时会出现相同的结果(无论如何,反引号是首选,因为我想要脚本的输出)。当我在命令行上的反引号之间运行确切的命令时,它完全按照预期工作。是什么原因造成的?
I'm writing a perl script that kicks off a script on several different servers using ssh. The remote script needs to run as long as this script is running:
#!/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";
}
}
[...]
When I run this script, I get the following output:
./brokenscript.pl
Running script on server01
$ tcsetattr: Input/output error
Connection to server01 closed.
The same result occurs when running with system
(and backticks are preferred anyways since I want the output of the script). When I run the exact command between the backticks on the command line, it works exactly as expected. What is causing this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当 ssh 尝试将本地终端置于“原始”模式(涉及调用 tcsetattr)时,
tcsetattr:输入/输出错误
消息来自 ssh em>; 请参阅sshtty.c
中的enter_raw_mode
,从clientloop.c
中的client_loop
调用)。来自 IEEE Std 1003.1, 2004 (Posix) 第 11.1.4 节:终端访问控制,如果调用进程处于孤立(或后台? ) 过程组。
实际上,ssh 正在尝试更改本地终端的设置,即使它不在前台进程组中(由于您的 fork 以及本地脚本退出(如证据所示)通过在引用的输出中紧邻错误消息之前出现的明显 shell 提示))。
如果您只是想避免错误消息,可以使用 ssh -ntt(从 /dev/null 重定向标准输入,但无论如何要求远程端分配 tty)而不是 ssh - t (当然,添加您的
-l
以及您需要的任何其他选项)。更有可能的是,只要某些远程进程仍在运行,您就有兴趣保持本地脚本运行。为此,您需要使用
wait
函数(或它的“亲戚”之一)等待每个分叉进程退出,然后再退出分叉它们的程序(这将使它们保留在前台进程组中,只要启动它们的程序在其中)。不过,您可能仍然想使用-n
,因为如果您分叉的多个 ssh 实例都尝试使用(读取或更改设置),则会造成混乱的)本地终端同时。作为一个简单的演示,您可以让本地脚本在分叉所有子进程后执行
sleep 30
操作,以便 ssh 命令在运行时有时间启动。前台进程组的一部分。这应该会抑制错误消息,但不会实现您既定的目标。您需要等待
(如果我正确地解释了您的目标)。The
tcsetattr: Input/output error
message comes from ssh when it tries to put the local terminal into “raw” mode (which involves a call to tcsetattr; seeenter_raw_mode
insshtty.c
, called fromclient_loop
inclientloop.c
).From IEEE Std 1003.1, 2004 (Posix) Section 11.1.4: Terminal Access Control, tcsetattr may return -1 with
errno == EIO
(i.e. “Input/output error”) if the calling process is in an orphaned (or background?) process group.Effectively ssh is trying to change the setting of the local terminal even though it is not in the foreground process group (due of your fork and, the local script exiting (as evidenced by the apparent shell prompt that comes immediately before the error message in your quoted output)).
If you just want to avoid the error message, you can use
ssh -ntt
(redirect stdin from /dev/null, but ask the remote side to allocate a tty anyway) instead ofssh -t
(add your-l
and any other options you need back in too, of course).More likely, you are interesting in keeping the local script running as long as some of the remote processes are still running. For that, you need to use the
wait
function (or one of its “relatives”) to wait for each forked process to exit before you exit the program that forked them (this will keep them in the foreground process group as long as the program that started them is in it). You may still want to use-n
though, since it would be confusing if the multiple instances of ssh that you forked all tried to use (read from, or change the settings of) the local terminal at the same time.As a simple demonstration, you could have the local script do
sleep 30
after forking off all the children so that the ssh command(s) have time to start up while they are part of the foreground process group. This should suppress the error message, but it will not address your stated goal. You needwait
for that (If I am interpreting your goal correctly).发生这种情况可能是因为当 stdin/stdout 不是真正的 tty 时,您强制 SSH 分配一个 tty。 SSH 尝试在这些处理程序上调用某些特定的 tty 函数(可能是从远程端转发的),并且调用失败并返回一些错误。
你有什么理由应该分配一个 tty 吗?
是否还有理由使用 SSH 协议的过时版本 1?That probably happens because you are forcing SSH to allocate a tty when stdin/stdout are not really ttys. SSH tries to call some specific tty function on those handlers (probably forwarded from the remote side) and the call fails returning some error.
Is there any reason why you should be allocating a tty?
Is there also any reason to use the obsolete version 1 of the SSH protocol?