我正在编写一个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
运行时会出现相同的结果(因为我想要脚本的输出,所以反正是首选的)。当我在命令行上的反引号之间运行确切的命令时,它完全按预期工作。造成这种情况的原因是什么?
答案 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?