1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
<?php // Execute command with PHP and read/write STDOUT, STDERR and STDIN $cmd = array( 'cwd' => '/home/user/Desktop', 'cmd' => '/home/user/Desktop/test.sh arg1 arg2 arg3', // 実行するコマンドのための環境変数の配列。 現在の PHP プロセスと同じ環境変数を使用する場合は NULL を指定します。 'env' => NULL, 'stdout' => '', 'stderr' => '', 'stdin' => array( '[\r\n]ABC[\r\n]' => "Yes\n", '[\r\n]DEF[\r\n]' => "No\n", ), ); try { $result = exec_cmd($cmd); print_r($result); } catch (Exception $e) { echo $e->getMessage(); } /** * See: http://php.net/manual/ja/function.proc-open.php * https://stackoverflow.com/questions/16351302/reading-from-stdin-pipe-when-using-proc-open * http://ishijima.blog97.fc2.com/blog-entry-68.html */ function exec_cmd($cmd, $timeout_sec = 3) { $timeout = time() + $timeout_sec; // sec $descriptorspec = array( 0 => array("pipe", "r"), // stdin read by child process 1 => array('pipe', 'w'), // stdout write by child process 2 => array('pipe', 'w') // stderr write by child process ); $pipes = NULL; $proc = proc_open($cmd['cmd'], $descriptorspec, $pipes, $cmd['cwd'], $cmd['env']); if (!is_resource($proc)) { return false; } // print_r($pipes); // non blocking, need to timeout. stream_set_blocking($pipes[0] ,0); stream_set_blocking($pipes[1] ,0); stream_set_blocking($pipes[2] ,0); $stdin_key = key($cmd['stdin']); $stdin_val = array_shift($cmd['stdin']); $match_string = ''; $close = function($proc, $pipes) { foreach ($pipes as &$pipe) { if ($pipe) { fclose($pipe); } } unset($pipes); return proc_close($proc); }; // now, poll for child termination while (true) { // echo 'while '; // detect if the child has terminated - the php way $status = proc_get_status($proc); // check return value if ($status === FALSE) { $close($proc, $pipes); throw new Exception ("Failed to obtain status information for ".$status['pid']); } if ($timeout - time() <= 0) { $close($proc, $pipes); throw new Exception ("Timeout ".$status['pid']); } if ($status['running'] === FALSE) { $cmd['exitcode'] = $status['exitcode']; $cmd['proc_close'] = $close($proc, $pipes); // print_r($cmd); return $cmd; } // read from child stdout and stderr // avoid *forever* blocking through using a time out (50000usec) foreach (array(1, 2) as $desc) { // echo 'foreach '; // check stdout for data $read = array($pipes[$desc]); $write = NULL; $except = NULL; $tv = 0; $utv = 50000; // microsecond $n = stream_select($read, $write, $except, $tv, $utv); if ($n == 0) { continue; } do { // echo 'do '; $data = fread($pipes[$desc], 8092); // fwrite(STDOUT, $data); if ($desc == 1) { $cmd['stdout'] .= $data; $match_string .= $data; } else { $cmd['stderr'] .= $data; } if ($stdin_key) { if (preg_match('/('.$stdin_key.')(.*)$/', $match_string, $matches)) { // echo '-- matches --'; // print_r($matches); $match_string = $matches[count($matches) - 1]; // Behind the matched part fwrite($pipes[0], $stdin_val); $stdin_key = key($cmd['stdin']); $stdin_val = array_shift($cmd['stdin']); } } } while (strlen($data) > 0); } // foreach stdout and stderr } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#!/bin/sh # test.sh pwd echo $* echo '123,' echo 'error-123,' >&2 echo '456,' echo 'error-456,' >&2 echo 'ABC' read -p "fool? (y/N): " yn echo $yn echo '789,' echo 'error-789,' >&2 echo 'DEF' read -p "cool? (y/N): " yn echo $yn echo '101112,' echo 'error-101112,' >&2 |