1

ベンダー提供の python ツールがあります (これはバイト コンパイルされており、ソースはありません)。このため、ベンダー提供の python 2.4 を使用することにも縛られています。ユーティリティへの道は次のとおりです。

source login.sh
oupload [options]

login.sh はいくつかの環境変数を設定し、次に 2 つのエイリアスを設定します。

odownload () {
${PYTHON_CMD} ${OCLIPATH}/ocli/commands/word_download_command.pyc "$@"
}
oupload () {
${PYTHON_CMD} ${OCLIPATH}/ocli/commands/word_upload_command.pyc "$@"
}

今、私がそれを彼らのやり方で実行すると、うまくいきます。ユーザー名とパスワードの入力を求めるプロンプトが表示されます。

ツールの周りにラッパーを作成して、実行後に追加の手順を実行し、ユーティリティに適切なデフォルトを提供しようとしています。私が直面している問題は、私の人生では、サブプロセスを使用してこれを成功させる方法を理解できないことです。元のコマンドが端末から直接実行されていないことに気付いたようで、ベイルします。

「/usr/local/bin/oupload」を作成し、元の login.sh からコピーしました。唯一の違いは、最後にエイリアスを実行する代わりに、実際にコマンドを実行することです。

次に、私の python スクリプトで、新しいシェル スクリプトを実行しようとします。

if os.path.exists(options.zipfile):
    try:
        cmd = string.join(cmdargs,' ')
        p1 = Popen(cmd, shell=True, stdin=PIPE)

しかし、私は得る:

Enter Opsware Username: Traceback (most recent call last):
File "./command.py", line 31, in main
File "./controller.py", line 51, in handle
File "./controllers/word_upload_controller.py", line 81, in _handle
File "./controller.py", line 66, in _determineNew
File "./lib/util.py", line 83, in determineNew
File "./lib/util.py", line 112, in getAuth
Empty Username not legal

Unknown Error Encountered                                              

SUMMARY:
Name: Empty Username not legal
Description: None

そのため、追加の改行が送信されているように見えました (すべてのオプションを削除しようとしましたが、役に立ちませんでした)。

stdin=PIPE を設定しないと、次のようになります。

Enter Opsware Username: Traceback (most recent call last):
File "./command.py", line 31, in main
File "./controller.py", line 51, in handle
File "./controllers/word_upload_controller.py", line 81, in _handle
File "./controller.py", line 66, in _determineNew
File "./lib/util.py", line 83, in determineNew
File "./lib/util.py", line 109, in getAuth
IOError: [Errno 5] Input/output error

Unknown Error Encountered                                              

shell=False および shell=True とともに p1.communicate、p1.stdin.write() を使用する他のバリエーションを試しましたが、ユーザー名とパスワードを適切に送信する方法を見つけようとしてもうまくいきませんでした. 最後の結果として、提供されたユーティリティのバイトコードを調べてみましたが、役に立ちませんでした。ユーティリティのメインルーチンを適切な引数で呼び出すと、スレッドエラーでコアダンプが発生しました。

最終的な考え - ユーティリティは、入力を「待機」しているように見えたくありません。シェルから実行すると、'Username' プロンプトで一時停止します。python の popen を実行すると、パスワードが指定されていないと仮定して、ただ燃えて終了します。stdinバッファをプリロードする方法を調べようとしました - プロセスが利用可能であればそこから読み取るかもしれないと考えましたが、それが可能かどうかはわかりませんでした。

私は pexpect の使用を避けようとしています。これは主に、ベンダーが提供するプリコンパイル済みライブラリのためにベンダーが提供する python 2.4 を使用する必要があり、スクリプトの配布を可能な限り最小限のフットプリントに維持しようとしているためです。しなければなりません、しなければなりませんが、私はむしろそれを使用したくありません (正直なところ、この状況で機能するかどうかもわかりません)。

他に何を試すことができるかについての考えは、最も高く評価されます。

アップデート

そこで、バイトコードをさらに掘り下げて、コンパイルされたコマンドに欠けているものを見つけ出すことで、これを解決しました。

ただし、これには 2 つの問題がありました。

  1. ベンダー コードは、呼び出されたときに、完了時に終了していました
  2. ベンダー コードは stdout に書き込んでおり、これを保存して操作する必要がありました (アップロードされた pkg の ID が含まれています)。ベンダー コードがまだユーザー名/パスワードを要求していたため、stdout をリダイレクトすることはできませんでした。

1 は、コードを try/except 句でラップすることで簡単に解決できました。

2 は、次のようなことを行うことで解決されました: https://stackoverflow.com/a/616672/677373

ログ ファイルの代わりに、cStringIO を使用しました。偽の「フラッシュ」メソッドも実装する必要がありました。これは、ベンダー コードがそれを呼び出していて、stdout 用に提供した新しい obj がそれを提供していないと不平を言っているように見えるためです。コードは次のようになります。

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = StringIO()

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)

    def flush(self):
        self.terminal.flush()
        self.log.flush()

if os.path.exists(options.zipfile):
    try:
        os.environ['OCLI_CODESET'] = 'ISO-8859-1'
        backup = sys.stdout
        sys.stdout = output = Logger()
        # UploadCommand was the command found in the bytecode
        upload = UploadCommand()
        try:
            upload.main(cmdargs)
        except Exception, rc:
            pass
        sys.stdout = backup
        # now do some fancy stuff with output from output.log

私が単に except: 節で 'pass' を実行する唯一の理由は、except 節が常に呼び出されることです。「rc」は実際にはコマンドからの戻りコードなので、ゼロ以外の場合の処理​​を追加する予定です。

4

2 に答える 2

0

stdinバッファをプリロードする方法を調べようとしました

名前付きの fifo を作成し、ユーザー名とパスワードの情報を入力してから、読み取りモードで再度開いて popen に渡しpopen(..., stdin=myfilledbuffer)ますか?

通常の一時ファイルを作成し、データを書き込み、読み取りモードで再度開き、再度開いたハンドルを stdin として渡すこともできます。(ユーザー名/パスワードを一時ファイルに書き込むことはしばしば悪いことなので、これは私が個人的に避けたいことです。OTOHはFIFOよりもテストが簡単です)

根本的な原因については、問題のソフトウェアがノンブロッキング メソッドを介して stdin から読み取っていると思われます。端末に接続したときになぜ機能するのかわかりません。

AAAANYWAY: Popen を介してパイプを直接使用する必要はまったくありませんよね? 私はこれのハックさをちょっと笑いますが、きっとうまくいくでしょう:

# you don't actually seem to need popen here IMO -- call() does better for this application.
statuscode = call('echo "%s\n%s\n" | oupload %s' % (username, password, options) , shell=True)

status = call('echo "foo\nbar\nbar\nbaz" |wc -l', shell = True)(出力は当然「4」です。)

于 2012-11-06T07:51:46.723 に答える
0

元の質問は、ターミナルを使用せずに問題を回避し、代わりにシェルスクリプトによって呼び出されていたpythonコードをインポートしてそれを使用することで解決されました。

ただし、JF Sebastian の回答は、最初に尋ねられたものに対しておそらくうまくいくと思います。そのため、同様の質問に対する回答を探している人は、pty モジュールを使用する道をたどることをお勧めします。

于 2012-11-07T22:29:32.627 に答える