我正在编写一个 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";
}
}
[...]
Run Code Online (Sandbox Code Playgroud)
当我运行这个脚本时,我得到以下输出:
./brokenscript.pl
在 server01 上运行脚本
$ tcsetattr:输入/输出错误
与 server01 的连接已关闭。
运行时会出现相同的结果system(无论如何,反引号是首选,因为我想要脚本的输出)。当我在命令行上的反引号之间运行确切的命令时,它完全按预期工作。这是什么原因造成的?
当ssh尝试将本地终端置于“原始”模式(这涉及调用tcsetattr;参见in ,从in调用)时,该tcsetattr: Input/output error消息来自ssh。enter_raw_modesshtty.cclient_loopclientloop.c
从 IEEE Std 1003.1, 2004 (Posix) Section 11.1.4: Terminal Access Control 开始,如果调用进程在孤立(或后台?)进程组中,tcsetattr可能会返回 -1 errno == EIO(即“输入/输出错误”)。
有效地ssh试图更改本地终端的设置,即使它不在前台进程组中(由于您的fork和本地脚本退出(正如在您的错误消息之前出现的明显 shell 提示所证明的那样)引用输出))。
如果您只想避免出现错误消息,您可以使用ssh -ntt(从 /dev/null 重定向 stdin,但无论如何要求远程端分配一个 tty)而不是ssh -t(添加您的-l和您需要的任何其他选项,当然)。
更有可能的是,只要某些远程进程仍在运行,您就会对保持本地脚本运行感兴趣。为此,您需要使用该wait函数(或其“亲戚”之一)在退出分叉它们的程序之前等待每个分叉进程退出(这将使它们保留在前台进程组中,只要程序开始他们在里面)。-n不过,您可能仍然想使用,因为如果您分叉的多个ssh实例都试图同时使用(读取或更改其设置)本地终端,那将会令人困惑。
作为一个简单的演示,您可以sleep 30在分叉所有子进程后让本地脚本执行此操作,以便ssh命令在它们是前台进程组的一部分时有时间启动。这应该会抑制错误消息,但它不会解决您的既定目标。您需wait要这样做(如果我正确解释了您的目标)。
| 归档时间: |
|
| 查看次数: |
5471 次 |
| 最近记录: |