6

コマンドが失敗したときにretry.pyと呼ばれるコマンドを繰り返すための単純なラッパー スクリプトを作成しました。ただし、子コマンドの出力を見たいので、いくつかの pty トリックをプルする必要がありました。これは rsync などのプログラムでは問題なく機能しますが、scp などの他のプログラムでは、進行状況メーターなどを表示するために追加のテストが適用されます。

scp コードには、大まかに次のようなテストがあります。

getpgrp() == tcgetpgrp(STDOUT_FILENO);

ラッパースクリプトを実行すると失敗します。私の単純な tty_test.c テスト ケースでわかるように:

./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

pty fd の IOCTL として終了する tcsetpgrp() を使用しようとしましたが、pty の -EINVAL が発生します。可能であれば Python サブプロセス機構を使い続けたいですか、それとも手動で fork/execve'ing する必要がありますか?

4

1 に答える 1

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 のフォアグラウンド グループにします。新しいプロセスは、SIGTTOUそれが発生したときに を取得し ( 以来、setpgrp()バックグラウンド プロセス グループにあったため)、通常はプロセスを停止させるため、 を無視するのはそのためですSIGTTOU。サブプロセスが予期しないシグナルテーブルによって混乱する可能性を最小限に抑えるために、SIGTTOUハンドラーを以前の状態に戻します。

サブプロセスは tty のフォアグラウンド グループにあるため、その tcgetpgrp() と getpgrp() は同じになり、isatty(1) は true になります (retry.py から継承する stdout が実際には tty であると仮定します)。サブプロセスと tty の間でトラフィックをプロキシする必要はありません。これにより、すべてのselectイベント処理と fcntl-nonblocking-setting を破棄できます。

于 2013-03-06T20:48:01.447 に答える