我一直在玩subprocess
模块来迭代发送
输入文件中的每一行到由以下命令创建的进程。
ssh -t -A $host 'remote_command'
remote_command
期望STDIN中的一行,对其进行一些处理
行并迭代循环直到STDIN关闭或达到EOF。
为实现这一目标,我一直在做的是:
process = subprocess.Popen("ssh -t -A $host 'remote_command'",
shell=True,
stdin=subprocess.PIPE)
for line in file('/tmp/foo'):
process.stdin.write(line)
process.stdin.flush()
process.stdin.close()
但我发现上述方法并不够健壮,因为它确实如此
通常情况是remote_command
过早完成而不处理
整个内容(尽管有时相同的代码确实成功而没有问题)。
当我采用另一种方法时,情况是一样的,虽然非常类似:
process = subprocess.Popen("ssh -t -A $host 'remote_command'",
shell=True,
stdin=file('/tmp/foo'))
所以问题是:如何确保输入文件中的每一行都被Python中的远程机器发送,接收和处理直到最后?
答案 0 :(得分:2)
如果这......
process = subprocess.Popen("ssh -t -A $host 'remote_command'",
shell=True,
stdin=subprocess.PIPE)
for line in file('/tmp/foo'):
process.stdin.write(line)
process.stdin.flush()
process.stdin.close()
...是你的整个计划,它不会(必然)有效。
尽管对process.stdin.close()
的最终调用将确保在程序终止之前已将所有数据发送到ssh
进程,但无法确保ssh
进程已发送网络上的所有数据,因此可能会有一些未完成的数据要发送。
不幸的是,由于ssh
进程是您程序的子进程,因此当您的程序终止时,ssh
进程将收到一个SIGHUP
,它会立即杀死它,可能在之前它完成了所有数据的发送。
只要remote_command
在点击EOF时终止,就不会出现问题,您可以要求ssh
进程忽略SIGHUP
,然后继续在后台运行与...
process = subprocess.Popen("nohup ssh -t -A $host 'remote_command'", ...)
...或者让您的程序等待ssh
进程完成,方法是添加...
process.wait()
...到你的程序结束。
<强>更新强>
经过进一步检查,如果某个流程的控制tty终止,而不是其父流程,则该流程看起来只会获得SIGHUP
。
这可能与-t
选项有关,该选项在远程主机上创建一个新的控制tty,并且在它生成的子进程完成之前就已退出。
在这种情况下,您可能需要......
process = subprocess.Popen("ssh -t -A $host 'nohup remote_command'", ...)
...或者在没有-t
选项的情况下尝试。
答案 1 :(得分:1)
您所做的不仅仅是为了确保所有输入都发送到您的子进程。在我看来,你的第二个例子比第一个好。您可以做的是检查子进程的返回代码。
return_code = p.wait()
您的远程命令应该在成功完成时返回0,如果发生错误则返回非零值。
答案 2 :(得分:0)
与使用paramiko之类的东西相比,你最好不要绕过子流程。
但在任何一种情况下,如果您在发送所有数据之前终止了连接,则可以捕获该异常,并且您将知道需要重试。如果进程过早死亡,您应该能够读取进程的退出代码。
答案 3 :(得分:0)
我想说最好的办法是使用回复管道来捕获远程命令的结果,并确保在每行之间和每行之间达到提示。 BTW我有时发现在远程链接会话结束时使用诸如ls -l之类的伪命令有助于确保在删除连接之前完成处理。