[英]Send Ctrl-C to remote processes started via subprocess.Popen and ssh
如何將Ctrl-C
發送到Popen()
對象中的多個ssh -t
進程?
我有一些Python代碼可以啟動遠程主機上的腳本:
# kickoff.py
# i call 'ssh' w/ the '-t' flag so that when i press 'ctrl-c', it get's
# sent to the script on the remote host. otherwise 'ctrol-c' would just
# kill things on this end, and the script would still be running on the
# remote server
a = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'a'])
a.communicate()
這很好用,但我需要啟動遠程主機上的多個腳本:
# kickoff.py
a = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'a'])
b = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'b'])
a.communicate()
b.communicate()
結果是Ctrl-C
無法可靠地殺死所有內容,之后我的終端總是出現亂碼(我必須運行'reset')。 那么當主要文件被殺死時如何殺死兩個遠程腳本呢?
注意:我正在嘗試避免登錄到遠程主機,在進程列表中搜索“script.sh”,並向兩個進程發送SIGINT。 我只是希望能夠在啟動腳本上按Ctrl-C
,並殺死兩個遠程進程。 一個不太理想的解決方案可能涉及確定性地找到遠程腳本的PID,但我不知道如何在我當前的設置中這樣做。
更新:在遠程服務器上啟動的腳本實際上啟動了幾個子進程,並且殺死ssh
確實會殺死原始遠程腳本(可能是SIGHUP的b / c),子任務不會被終止。
我成功殺死所有子進程的唯一方法是使用pexpect :
a = pexpect.spawn(['ssh', 'remote-host', './script.sh', 'a'])
a.expect('something')
b = pexpect.spawn(['ssh', 'remote-host', './script.sh', 'b'])
b.expect('something else')
# ...
# to kill ALL of the children
a.sendcontrol('c')
a.close()
b.sendcontrol('c')
b.close()
這足夠可靠。 我相信其他人早些時候發布了這個答案,但后來刪除了答案,所以我會發布它以防其他人好奇。
當被殺死時,ssh會向遠程進程發送一個SIGHUP。 您可以將遠程進程包裝到shell或python腳本中,當腳本收到SIGHUP時會將其終止(請參閱bash的trap命令和python中的信號模塊)
甚至可以使用膨脹的命令行而不是遠程包裝腳本來執行此操作。
問題是殺死遠程進程不是你想要的,你想要的是在你做Ctrl + C后有一個工作終端。 要做到這一點,你必須殺死遠程進程並查看剩余的輸出,其中包含一些終端控制序列以將終端重置為正確的狀態。 為此,您需要一個機制來表示包裝器腳本以終止進程。 這不是一回事。
我沒有試過這個,但也許你可以捕獲KeyboardInterrupt然后殺死進程:
try
a = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'a'])
b = subprocess.Popen(['ssh', '-t', 'remote-host', './script.sh', 'b'])
a.communicate()
b.communicate()
except KeyboardInterrupt:
os.kill(a.pid, signal.SIGTERM)
os.kill(b.pid, signal.SIGTERM)
我通過取消映射我關心的所有信號來解決類似的問題。 當按下Ctrl + C時,它仍將被傳遞到子進程,但是Python將等到子進程退出之后再處理主腳本中的信號。 只要子進程響應Ctrl + C,這對於信號子進程就可以正常工作。
class DelayedSignalHandler(object):
def __init__(self, managed_signals):
self.managed_signals = managed_signals
self.managed_signals_queue = list()
self.old_handlers = dict()
def _handle_signal(self, caught_signal, frame):
self.managed_signals_queue.append((caught_signal, frame))
def __enter__(self):
for managed_signal in self.managed_signals:
old_handler = signal.signal(managed_signal, self._handle_signal)
self.old_handlers[managed_signal] = old_handler
def __exit__(self, *_):
for managed_signal, old_handler in self.old_handlers.iteritems():
signal.signal(managed_signal, old_handler)
for managed_signal, frame in self.managed_signals_queue:
self.old_handlers[managed_signal](managed_signal, frame)
現在,我的子進程代碼如下所示:
with DelayedSignalHandler((signal.SIGINT, signal.SIGTERM, signal.SIGHUP)):
exit_value = subprocess.call(command_and_arguments)
每當按下Ctrl + C時,允許應用程序在處理信號之前退出,因此您不必擔心終端會出現亂碼,因為子進程線程沒有與主進程線程同時終止。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.