2016-03-06 10 views
6

コマンドラインのPHPスクリプトで実行するプログラムとどのように対話できるかを知りたい。シナリオは次のとおりです。PHPのコマンドラインで実行されるプログラムからの質問に応答する

  1. プログラムの実行を開始します。
  2. 質問が尋ねられるまで(私が推測するSTDOUTを読むことによって)出力を読みます。
  3. 答えを入力し、Enterキーを押します(これはSTDINに書き込むと思います)。ユーザーはこれを入力しないでください。スクリプトは、ステップ2の出力を読み取って解釈することで、何を回答するかをすでに知っています。
  4. 新しい質問が表示されるまで、出力を再度読みます。
  5. もう一度答えを入力し、Enterキーを押します。ここでも、スクリプトはそれをすべて知っているため、ユーザの入力は行われません。
  6. この質問/回答のシナリオは、プログラムが終了するまでx回繰り返します。

これを行うPHPスクリプトを作成するにはどうすればよいですか?私はおそらくproc_open()を使用したいと思っていますが、私はどのように把握できません。私はそれがこのようなものになるだろうと思っていますが、それはもちろん動作しません:

$descriptorspec = array(
    0 => array('pipe', 'r'), //STDIN 
    1 => array('pipe', 'w'), //STDOUT 
    2 => array('pipe', 'r'), //STDERR 
); 
$process = proc_open('mycommand', $descriptorspec, $pipes, null, null); 
if (is_resource($process)) { 
    // Get output until first question is asked 
    while ($buffer = fgets($pipes[1])) { 
     echo $buffer; 
    } 
    if (strpos($buffer, 'STEP 1:') !== false) { 
     fwrite($pipes[0], "My first answer\n"); //enter the answer 
    } else { 
     die('Unexpected last line before question'); 
    } 

    // Get output until second question is asked 
    while ($buffer = fgets($pipes[1])) { 
     echo $buffer; 
    } 
    if (strpos($buffer, 'STEP 2:') !== false) { 
     fwrite($pipes[0], "My second answer\n"); //enter the answer 
    } else { 
     die('Unexpected last line before question'); 
    } 

    // ...and so we continue... 
} else { 
    echo 'Not a resource'; 
} 

UPDATE:私はプログラムがSTDERRに質問を出力することを考え出し(それがファイルにSTDOUTを書き込むため) 。

+0

これをチェックしてください:https://stackoverflow.com/questions/2929629/how-do-i-write-a-command-line-interactive-php-script – jsxqf

+0

@jsxqfあなたは私の質問を読んでいません。私がPHPとやりとりすることではありません。それはPHPが別のプログラムとやりとりすることです(私の入力なし)。 – TheStoryCoder

+0

外部プログラムは、 "STEP 1:"と "STEP 2:"の直後と応答を待つ前に改行( "\ n")を出力しますか?また、質問は常に同じで、同じ順序ですか? –

答えて

0

上記のコードに基づいて次の例を試してみてください。

使用しているオペレーティングシステムが指定されていませんが、私はLinuxでテストしましたが、 となります。

コマンドは別のプロセスにパイプされるため、出力はバッファに格納されます。 これは、現在の行を送信する前にバッファが 改行を永久に待機し、プロンプトに改行が続いていないために、 改行が表示されないため、プロンプトが表示されないことを意味します。 Trick an application into thinking its stdin is interactive, not a pipe にクリスとignomueller.netの 答えに

おかげで、我々はそれがコマンドの出力の写し(typescript)を生成scriptへの引数としてそれを を渡すことで、端末に話しているという考えにコマンドを混同することができます。 タイプスクリプトを/ dev/nullに保存すると、書き込み(-f)と (開始メッセージと完了メッセージを除く)の後に出力をフラッシュする(-q)ので、プロンプトを読むことができるので、 が出力されます。

コマンドからのSTDOUTをファイルに送信するように指定しましたが、 の質問にはSTDERRの質問が表示されます。これは、私が使用した 論理がSTDERRからの読取りを動作させていないように見えるため、追加された合併症です。しかし、 STDOUTを-cパラメータ内のファイルにリダイレクトしてscriptとし、script自身の STDERRをSTDOUTにリダイレクトすると、すべて正常に動作しているようです。

$descriptorspec = array(
    0 => array('pipe', 'r'), //STDIN 
    1 => array('pipe', 'w'), //STDOUT 
    2 => array('pipe', 'r'), //STDERR 
); 
// Avoid buffering by passing the command through "script" 
$process = proc_open(
    'script -qfc "mycommand >mycommand.out" /dev/null 2>&1', 
    $descriptorspec, $pipes, null, null); 
if (is_resource($process)) { 
    $buffer = ""; 
    // Read from the command's STDOUT until it's closed. 
    while (!feof($pipes[1])) { 
     $input = fread($pipes[1], 8192); 
     $buffer .= $input; 

     // Output what we've read for debugging. You'd want to add some logic here 
     // instead to handle the input and work out the answers to the questions. 
     echo $input; 

     // Answer the questions when appropriate. 
     // This won't work if your output ever includes "STEP 1:" other than when 
     // prompting for a question. You might need some more robust logic here. 
     if (preg_match("/\nSTEP 1:$/", $buffer)) { 
      fwrite($pipes[0], "My first answer\n"); 
     } elseif (preg_match("/\nSTEP 2:$/", $buffer)) { 
      fwrite($pipes[0], "My second answer\n"); 
     } 
    } 
    proc_close($process); 
} else { 
    echo 'Not a resource'; 
} 

プロンプトへの回答に「出力の読み取りと解釈」が必要であると指定したため、この方法でコードを記述しました。プログラムの実行に先立って答えがわかっていれば、その解決法ははるかに簡単です。この場合、応答を読み始める前にすべての回答をすぐに出力することができます。このコマンドは、入力を提供する前にプロンプ​​トが表示されるのを待たなかったことに気を付けません。この場合、stream_set_blocking($pipes[0], false);を最初に呼び出す必要があるかもしれませんが、私は100%確実ではありません。

0

あなたは確かに正しい軌道に乗っています。

それはあなたのコード内の明白な問題を開始するのが最善かもしれません:

while ($buffer = fgets($pipes[1])) { 
    echo $buffer; 
} 

このループを終了することはありません。ある時点でプログラムはあなたに質問をしますが、あなたのコードはまだ(ブロックしている)fgets呼び出しを実行しています。正しく動作するコードを書く方法については

....

最も明白な解決策は、あなたの答えを提供する前にプロンプ​​トを待っている気にしないことです。あなたはプログラムが標準入力から読み込んでいるとのバッファをクリアしない

  • プログラムの前の出力に基づいて、あなたの応答を適応させる必要はありません

    1. :これは限り動作します任意のポイント

    実際には、あなたも、これに制御プロセスを必要としない:

    program <input.txt >output.txt 2>errors.txt 
    

    しかし、適用されない1および/または2を想定し、

    ... 
    if (is_resource($process)) { 
        while ($buffer=fgets($pipes[2]) { // returns false at EOF/program termination 
         if (strpos($buffer, 'STEP 1:') !== false) { 
          fwrite($pipes[0], "My first answer\n");  
         } else if (strpos($buffer, 'STEP 2:') !== false) { 
          fwrite($pipes[0], "My second answer\n"); //enter the answer 
         } 
        } 
    } 
    

    は外のための検査を実施し、(種類の私たちが知っているよりも、話に多くのがあります示唆している)、すでにその標準出力をリダイレクトしていることを考えますシーケンス質問と要求/応答サイクルでの分岐は、読者の練習として残されています。

  • 関連する問題