2011-05-13 123 views
3

我正在编写一个perl脚本,它启动几个不同服务器上的脚本使用ssh。远程脚本需要,只要运行,因为这脚本运行:Perl的反引号/系统给出“tcsetattr:输入/输出错误”

#!/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一起运行时会发生相同的结果(因为我希望输出脚本,反引用是首选。当我在命令行反引号之间运行确切的命令时,它完全按预期工作。这是什么造成的?

回答

4

tcsetattr: Input/output error消息来自SSH当它试图把本地终端到“原始”模式(其涉及到tcsetattr呼叫;参见sshtty.center_raw_mode,在clientloop.cclient_loop调用)。

从IEEE标准1003.1,2004(POSIX)Section 11.1.4: Terminal Access Controltcsetattr可以返回-1与errno == EIO(即“输入/输出错误”)如果调用进程是一个孤立的(或背景?)处理组中使用。

有效SSH正在试图改变该本地终端的设置,即使它不是前台进程组中(由于你和的,本地脚本离开(通过表观壳提示证明该紧接在引用输出中的错误消息之前))。

如果你只是想避免的错误信息,您可以使用ssh -ntt(重定向从/ dev/null的标准输入,但要求远程方反正分配一个tty),而不是ssh -t(添加-l和任何其他选项,你当然也需要回来)。


更有可能的是,只要某些远程进程仍在运行,您很有可能保持本地脚本运行。为此,您需要使用wait函数(或其“亲属”之一)等待每个分支进程退出,然后才能退出分支它们的程序(只要程序存在,这将使它们保持在前台进程组中开始他们在里面)。尽管如此,如果您分叉的ssh的多个实例都尝试同时使用(从本地终端读取或更改设置),那么您仍可能仍希望使用-n

举一个简单的演示,你可以在本地脚本做sleep 30分叉了所有的孩子,让该SSH指令(S)有时间来启动,而他们是前台进程组的一部分了。这应该抑制错误信息,但它不会解决您陈述的目标。你需要wait(如果我正确解释你的目标)。

+0

星期一我来试试你的建议。在等待:远程脚本永远运行直到它们在这个脚本被杀时被杀死,所以我不认为我需要等待它们。但我将不得不进行调查。 – 2011-05-14 14:39:06

0

这可能是因为当标准输入/标准输出不是真正的ttys时,你迫使SSH分配一个tty。 SSH尝试在这些处理程序上调用某些特定的tty函数(可能从远程端转发),并且调用失败返回一些错误。

你有什么理由为什么你应该分配一个tty?

是否有任何理由使用SSH协议的废弃版本1?

+2

我认为这是一个呃,即登录选项,而不是一个。 – Mikel 2011-05-13 21:15:12

+0

@Mikel:哎呀,是的,你是对的! – salva 2011-05-13 21:22:02

+1

分配一个tty因为没有它,当这个脚本被杀死时,远程进程继续运行。当这个人被杀时,我需要他们被杀,而且 - 我知道这是唯一的方法。 – 2011-05-14 14:40:05