2016-03-06 57 views
6

的命令行上運行的程序提出的問題我想知道如何與我在命令行PHP腳本中運行的程序進行交互。該方案是:回答在PHP

  1. 啓動執行程序。
  2. 閱讀輸出,直到提問(通過閱讀標準輸出,我猜)。
  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'; 
} 

更新:我想通了,該程序的輸出問題STDERR(因爲它寫到標準輸出到文件) 。

+0

檢查了這一點:https://stackoverflow.com/questions/2929629/how-do-i-write-a-command-line-interactive-php-script – jsxqf

+0

@jsxqf你沒有看過我的問題。這不關於我與PHP交互。這是關於PHP與另一個程序交互(沒有任何來自我的輸入)。 – TheStoryCoder

+0

您的外部程序是否在「步驟1:」和「步驟2:」之後立即輸出換行符(「\ n」),然後再等待響應?另外,這些問題是否始終相同並且順序相同? –

回答

0

希望根據您的代碼下面的例子以上就足以讓你開始。

我已經在Linux上測試過它,雖然你沒有指定你使用的是哪個操作系統,所以你的里程可能會有所不同,如果你在運行別的東西。

由於該命令被管道輸送到另一個過程中,輸出將被緩衝。 這意味着因爲緩衝區永遠等待 換行發送當前行之前和提示後不跟 換行符我們將不會收到提示。

感謝Chris和ignomueller.net的 答案Trick an application into thinking its stdin is interactive, not a pipe 我們可以迷惑命令以爲它通過傳遞 它作爲參數傳遞給script,產生命令輸出的打字稿跟一個終端。 通過每次寫入(-f)和 排除啓動和done消息(-q)後保存打字稿到/ dev/null的,沖洗的輸出,這意味着我們可以讀到的提示,因爲他們 輸出。

您指定應將命令的STDOUT發送到文件,而在STDERR上提示 問題。這是一個額外的複雜因素,因爲我用過的邏輯 似乎不能讀取STDERR。但是,如果您重定向 STDOUT向script-c參數中的文件,然後重定向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. 你並不需要根據程序的上面的輸出

  2. 程序從標準輸入它的閱讀和在不清除緩衝區上,以適應你的迴應:只要這將工作任何一點

事實上,你甚至不需要一個控制過程,以這樣的:

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 
     } 
    } 
} 

的出實施檢查序列問題和請求/響應週期中的分支作爲練習留給讀者。