2013-03-04 60 views
6

我寫了一個簡單的包裝腳本,用於重複命令,當它們失敗時調用retry.py。然而,因爲我想看到孩子命令的輸出,我不得不提一些竅門。這適用於像rsync這樣的程序,但像scp這樣的其他程序可以使用額外的測試來顯示諸如進度表之類的內容。如何爲我在pty下運行的進程設置終端前臺進程組?

的SCP代碼有一個測試,是廣:

getpgrp() == tcgetpgrp(STDOUT_FILENO); 

當我運行雖然包裝腳本失敗。

./tty_tests 
isatty reports 1 
pgrps are 13619 and 13619 

和:

./retry.py -v -- ./tty_tests 
command is ['./tty_tests'] 
isatty reports 1 
pgrps are 13614 and -1 
child finished: rc = 0 
Ran command 1 times 

我使用tcsetpgrp(嘗試),這對pty FD的但最終作爲IOCTL你可以用我的簡單tty_test.c的測試案例看導致ptys的-EINVAL。我寧願繼續使用Python子進程機制,如果可能的話,或者是手動fork/execve將會被需要嗎?

回答

9

我相信你能削減你的程序到這一點,如果你不需要提供一個全新的PTY的子流程:

from argparse import ArgumentParser 
import os 
import signal 
import subprocess 
import itertools 

# your argumentparser stuff goes here 

def become_tty_fg(): 
    os.setpgrp() 
    hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) 
    tty = os.open('/dev/tty', os.O_RDWR) 
    os.tcsetpgrp(tty, os.getpgrp()) 
    signal.signal(signal.SIGTTOU, hdlr) 

if __name__ == "__main__": 
    args = parser.parse_args() 

    if args.verbose: print "command is %s" % (args.command) 
    if args.invert and args.limit==None: 
     sys.exit("You must define a limit if you have inverted the return code test") 

    for run_count in itertools.count(): 
     return_code = subprocess.call(args.command, close_fds=True, 
             preexec_fn=become_tty_fg) 
     if args.test == True: break 
     if run_count >= args.limit: break 
     if args.invert and return_code != 0: break 
     elif not args.invert and return_code == 0: break 

    print "Ran command %d times" % (run_count) 

setpgrp()調用在同一會話創建一個新的進程組,這樣新的進程將接收到用戶的任何ctrl-c/ctrl-z/etc,而你的重試腳本不會。然後tcsetpgrp()使新進程組成爲控制tty上的前臺。當發生這種情況時(因爲setpgrp()已在後臺進程組中),新進程將獲得SIGTTOU,這通常會使進程停止,所以這就是忽略SIGTTOU的原因。我們將SIGTTOU處理程序恢復到之前的狀態,以最大程度地減少子進程被意外信號表混淆的機會。由於子進程現在處於tty的前臺組中,因此其tcgetpgrp()和getpgrp()將是相同的,並且isatty(1)將爲true(假設它從retry.py繼承的stdout實際上是一個tty)。您不需要在子流程和tty之間進行流量代理,這樣您就可以拋棄所有的事件處理和fcntl-nonblocking-setting。

+0

我給了一個嘗試,它沒有任何影響: > retry.py -v - 〜/ mysrc/retry.git/tty_tests 命令是['/home/ajb/mysrc/retry.git/tty_tests'] isatty reports 1 pgrps是28268和-1 孩子完成:rc = 0 Ran命令1次 – stsquad 2013-03-12 12:12:12

+0

您能否粘貼一些完整的代碼? – 2013-03-12 17:08:52

+0

OH!我只注意到你在你的問題中給出了retry.py的鏈接。我原以爲這只是stackoverflow試圖有幫助,並做出一個看起來像一個主機名的東西的鏈接。我會看一看。 – 2013-03-12 17:10:13