通过perl中的ssh将外部命令错误定向到文件

时间:2016-07-20 02:37:39

标签: bash perl ssh

这实际上对我有用,但突然间它停止工作。现在坚持了一个小时,但仍然不知道为什么。

我有

 system("ssh -t machine command > stdout.log 2> error.log &")

始终收到错误Pseudo-terminal will not be allocated because stdin is not a terminal

=== UPDATE ===

我已经尝试-T(这根本不会给我任何错误)和-t -t而不是-t并且不会在error.log中指示错误

=== MORE UPDATE ===

-t -t-tt给出了错误“tcgetattr:设备serverCmd ='ssh -x -n machine'的不适当的ioctl,我的目标程序根本没有启动。 只要&

,删除qx或使用Pseudo-terminal will not be allocated because stdin is not a terminal即会向我发送同样的错误-t

1 个答案:

答案 0 :(得分:0)

使用fork + exec(推荐),或使用管道打开,或删除-t伪终端

这里有很多事情要发生。您启动ssh,需要处理(伪)终端业务,处理流 - 以及后台进程。这是最后一点让它变得棘手。我得到了和你一样的行为。

以下是三个适用于我的解决方案/解决方法。我推荐第一个。

&的要点是你的脚本立即获得控制权。我们可以很好地在脚本中实现这一点。这更清晰,并不依赖于系统细节。所以放下&并自己将(ssh)进程放在后台。

use warnings;
use strict;

my $cmd = 'ssh -t machine command  >stdout.log 2>error.log';

my $pid = fork;
die "Can't fork: $!" if not defined $pid;

if ($pid == 0) {  # child
    exec($cmd);
    die "exec shouldn't have returned: $! ";
}
# Parent only. Child became $cmd, which went its own way, never to return.
# Rest of your code, executed right away ...

error.log可能包含行Connection to machine closed.(它适用于我)。如果需要,fork + exec的基本解释就在最后。

以下是可能存在问题的解决方法,可能无法在任何地方使用,但它们可以实现我的测试所需。

  • 如果command不需要控制tty drop -t。重定向和&仍适用于我。

  • 通过使用管道打开启动(ssh)进程来解决STDIN问题

    my $cmd = 'ssh -t machine command >stdout.log 2>error.log &';
    my $pid = open my $fh, '|-', $cmd // die "Can't fork: $!;
    # your other code ...
    close $fh;
    

    这个'打开'(分叉)执行$cmd的进程,以便$fhSTDIN,我们的脚本可以根据需要提供输入。所以我们提供STDIN和ssh的伪终端没问题。如果该命令不需要输入,您可以忽略该命令,其余部分根据需要工作。

    这是一种非常干净的方法,可以将另一个进程和管道分配到其STDINSTDOUT,或者从$pidif ($pid == 0) { }分配,请参阅教程perlopentut,参考open以及更多详细信息Safe Pipe Opens in perlipc。但是,在这种情况下,我宁愿选择上面的fork + exec。

fork创建一个新进程并返回其ID。这个新进程是父进程的一个克隆,除了对于该进程,if为零,而对于父进程,它是一个(大)整数。 (还有一些其他小得多的差异。)因此,孩子确实进入下一个ssh区块,而父组则没有。这样我们就可以分开子节点和父节点,因此每个节点都可以运行专用于它们的代码。我们得到了并行执行(原则上,也主要是在实践中)。

exec块内,子进程执行的程序被exec执行的程序完全替换,在本例中由if命令执行。 import commands iStat, askpassPath = commands.getstatusoutput("which ssh-askpass") cmd = "export SUDO_ASKPASS=%s;sudo -A mkdir -p /usr/lib/test"%(askpassPath) commands.getstatusoutput(cmd) 根本不会返回(除非调用本身失败)。父进程继续在static之后运行,正常执行任何没有阻塞的代码。通过这种方式,我们分拆了另一个程序,而您的程序可以继续其业务,而无需等待另一个程序完成。