我有一个perl脚本script.pl,当运行时,它会执行一个fork,父进程将其pid输出到一个文件,然后在子进程向STOUT输出内容然后进入while循环时退出。
$pid = fork();
if ( ! defined $pid )
{
die "Failed to fork.";
}
#Parent process
elsif($pid)
{
if(!open (PID, ">>running_PIDs"))
{
warn "Error opening file to append PID";
}
print PID "$pid \n";
close PID;
}
#child process
else
{
print "Output started";
while($loopControl)
{
#Do some stuff
}
}
当我在本地调用它时,这工作正常,即:perl script.pl。
脚本打印出一些东西,然后将控制权返回给shell。 (当子进程在后台进入循环时)。
然而,当我通过ssh控制它时,控件永远不会返回到shell(也不会打印出“Output started”行。
即: $ ssh username@example.com'perl script.pl'
然而,有趣的是,子进程确实运行(当我输入ps时我可以看到它)。
任何人都可以解释最新情况吗?
编辑:
我在调试下运行它得到了这个:
###分叉,但不知道如何创建新的TTY。由于两个调试器争用相同的TTY,输入严重纠缠。
我知道如何在xterms中将输出切换到不同的窗口 和OS / 2控制台。对于手动切换,请输入已创建的TTY的名称 在$ DB :: fork_TTY中,或定义一个函数DB :: get_fork_TTY()返回此。
在类UNIX系统上,可以获得给定窗口的TTY名称 通过键入tty,并通过sleep 1000000将shell与TTY断开连接。
答案 0 :(得分:9)
每当您通过非交互式ssh命令启动后台作业时,您需要关闭或以其他方式绑定stdin,stdout和&标准错误。否则ssh将等待后台进程退出。 FAQ
这称为取消与控制终端的关联或分离,是编写后台作业时的一般最佳做法,而不仅仅是SSH。
因此,最简单的更改不会使整个命令静音:添加:
#close std fds inherited from parent
close STDIN;
close STDOUT;
close STDERR;
在print "Output started";
之后。如果您的子进程需要在运行期间定期打印输出,那么您将需要重定向到日志文件。
答案 1 :(得分:4)
ssh username@example.com 'nohup perl script.pl'
您无法退出,因为仍然附加了一个流程。您需要nohup
。
答案 2 :(得分:2)
正在发生的事情是ssh直接执行'perl script.pl'作为命令。如果你有“屏幕”,你可以这样做:
$ ssh username@example.com 'screen -d -m perl script.pl'
让它在分离的屏幕上运行,稍后使用screen -r
重新连接答案 3 :(得分:0)
为了更好地理解这一点,我建议您阅读@Jax的解决方案
Getting ssh to execute a command in the background on target machine
这与Perl无关。这是因为SSH处理您尝试在后台运行的任何长时间运行的进程的方式。
我需要从bash脚本启动script.pl(以在目标主机上定义基本的本地变量):
$ ssh username@example.com /path/to/launcher.sh
/path/to/launcher.sh使用以下命令调用Perl脚本:
CMD="/path/to/script.pl -some_arg=$VALUE -other_arg"
$CMD &
在本地工作,但是通过ssh运行时没有返回。
我在Perl脚本中尝试了@pra的解决方案,但在我的情况下不起作用。
使用@Jax的解决方案,我将$CMD &
替换为:
nohup $CMD > /path/to/output.log 2>&1 < /dev/null &
而且效果很好。