// Run a command and return its results as a string. $output_string = shell_exec('program args'); // Same as above, using backtick operator. $output_string = `program args`; // Run a command and return its results as a list of strings, // one per line. $output_lines = array(); exec('program args', $output_lines); // ----------------------------- // The only way to execute a program without using the shell is to // use pcntl_exec(). However, there is no way to do redirection, so // you can't capture its output. $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { pcntl_waitpid($pid, $status); } else { // Note that pcntl_exec() automatically prepends the program name // to the array of arguments; the program name cannot be spoofed. pcntl_exec($program, array($arg1, $arg2)); } |
// Run a simple command and retrieve its result code. exec("vi $myfile", $output, $result_code); // ----------------------------- // Use the shell to perform redirection. exec('cmd1 args | cmd2 | cmd3 >outfile'); exec('cmd args <infile >outfile 2>errfile'); // ----------------------------- // Run a command, handling its result code or signal. $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { pcntl_waitpid($pid, $status); if (pcntl_wifexited($status)) { $status = pcntl_wexitstatus($status); echo "program exited with status $status\n"; } elseif (pcntl_wifsignaled($status)) { $signal = pcntl_wtermsig($status); echo "program killed by signal $signal\n"; } elseif (pcntl_wifstopped($status)) { $signal = pcntl_wstopsig($status); echo "program stopped by signal $signal\n"; } } else { pcntl_exec($program, $args); } // ----------------------------- // Run a command while blocking interrupt signals. $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { // parent catches INT and berates user declare(ticks = 1); function handle_sigint($signal) { echo "Tsk tsk, no process interruptus\n"; } pcntl_signal(SIGINT, 'handle_sigint'); while (!pcntl_waitpid($pid, $status, WNOHANG)) {} } else { // child ignores INT and does its thing pcntl_signal(SIGINT, SIG_IGN); pcntl_exec('/bin/sleep', array('10')); } // ----------------------------- // Since there is no direct access to execv() and friends, and // pcntl_exec() won't let us supply an alternate program name // in the argument list, there is no way to run a command with // a different name in the process table. |
// Transfer control to the shell to run another program. pcntl_exec('/bin/sh', array('-c', 'archive *.data')); // Transfer control directly to another program. pcntl_exec('/path/to/archive', array('accounting.data')); |
// Handle each line in the output of a process. $readme = popen('program arguments', 'r'); while (!feof($readme)) { $line = fgets($readme); if ($line === false) break; // ... } pclose($readme); // ----------------------------- // Write to the input of a process. $writeme = popen('program arguments', 'w'); fwrite($writeme, 'data'); pclose($writeme); // ----------------------------- // Wait for a process to complete. $f = popen('sleep 1000000', 'r'); // child goes to sleep pclose($f); // and parent goes to lala land // ----------------------------- $writeme = popen('program arguments', 'w'); fwrite($writeme, "hello\n"); // program will get hello\n on STDIN pclose($writeme); // program will get EOF on STDIN // ----------------------------- // Output buffering callback that sends output to the pager. function ob_pager($output, $mode) { static $pipe; if ($mode & PHP_OUTPUT_HANDLER_START) { $pager = getenv('PAGER'); if (!$pager) $pager = '/usr/bin/less'; // XXX: might not exist $pipe = popen($pager, 'w'); } fwrite($pipe, $output); if ($mode & PHP_OUTPUT_HANDLER_END) { pclose($pipe); } } // Redirect standard output to the pager. ob_start('ob_pager'); // Do something useful that writes to standard output, then // close the output buffer. // ... ob_end_flush(); |
// Output buffering: Only display a certain number of lines of output. class Head { function Head($lines=20) { $this->lines = $lines; } function filter($output, $mode) { $result = array(); $newline = ''; if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") { $newline = "\n"; $output = substr($output, 0, -1); } foreach (explode("\n", $output) as $i => $line) { if ($this->lines > 0) { $this->lines--; $result[] = $line; } } return $result ? implode("\n", $result) . $newline : ''; } } // Output buffering: Prepend line numbers to each line of output. class Number { function Number() { $this->line_number = 0; } function filter($output, $mode) { $result = array(); $newline = ''; if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") { $newline = "\n"; $output = substr($output, 0, -1); } foreach (explode("\n", $output) as $i => $line) { $this->line_number++; $result[] = $this->line_number . ': ' . $line; } return implode("\n", $result) . $newline; } } // Output buffering: Prepend "> " to each line of output. class Quote { function Quote() { } function filter($output, $mode) { $result = array(); $newline = ''; if (strlen($output) > 0 && $output[strlen($output) - 1] == "\n") { $newline = "\n"; $output = substr($output, 0, -1); } foreach (explode("\n", $output) as $i => $line) { $result[] = "> $line"; } return implode("\n", $result) . $newline; } } // Use arrays as callbacks to register filter methods. ob_start(array(new Head(100), 'filter')); ob_start(array(new Number(), 'filter')); ob_start(array(new Quote(), 'filter')); // Act like /bin/cat. while (!feof(STDIN)) { $line = fgets(STDIN); if ($line === false) break; echo $line; } // Should match number of calls to ob_start(). ob_end_flush(); ob_end_flush(); ob_end_flush(); |
// Process command-line arguments using fopen(). PHP supports URLs for // filenames as long as the "allow_url_fopen" configuration option is set. // // Valid URL protocols include: // - http://www.myserver.com/myfile.html // - ftp://ftp.myserver.com/myfile.txt // - compress.zlib://myfile.gz // - php://stdin // // See http://www.php.net/manual/en/wrappers.php for details. // $filenames = array_slice($argv, 1); if (!$filenames) $filenames = array('php://stdin'); foreach ($filenames as $filename) { $handle = @fopen($filename, 'r'); if ($handle) { while (!feof($handle)) { $line = fgets($handle); if ($line === false) break; // ... } fclose($handle); } else { die("can't open $filename\n"); } } |
$output = `cmd 2>&1`; // with backticks // or $ph = popen('cmd 2>&1'); // with an open pipe while (!feof($ph)) { $line = fgets($ph); } // plus a read // ----------------------------- $output = `cmd 2>/dev/null`; // with backticks // or $ph = popen('cmd 2>/dev/null'); // with an open pipe while (!feof($ph)) { $line = fgets($ph); } // plus a read // ----------------------------- $output = `cmd 2>&1 1>/dev/null`; // with backticks // or $ph = popen('cmd 2>&1 1>/dev/null'); // with an open pipe while (!feof($ph)) { $line = fgets($ph); } // plus a read // ----------------------------- $output = `cmd 3>&1 1>&2 2>&3 3>&-`; // with backticks // or $ph = popen('cmd 3>&1 1>&2 2>&3 3>&-|'); // with an open pipe while (!feof($ph)) { $line = fgets($ph); } // plus a read // ----------------------------- exec('program args 1>/tmp/program.stdout 2>/tmp/program.stderr'); // ----------------------------- $output = `cmd 3>&1 1>&2 2>&3 3>&-`; // ----------------------------- $fd3 = $fd1; $fd1 = $fd2; $fd2 = $fd3; $fd3 = null; // ----------------------------- exec('prog args 1>tmpfile 2>&1'); exec('prog args 2>&1 1>tmpfile'); // ----------------------------- // exec('prog args 1>tmpfile 2>&1'); $fd1 = "tmpfile"; // change stdout destination first $fd2 = $fd1; // now point stderr there, too // ----------------------------- // exec('prog args 2>&1 1>tmpfile'); $fd2 = $fd1; // stderr same destination as stdout $fd1 = "tmpfile"; // but change stdout destination |
// Connect to input and output of a process. $proc = proc_open($program, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w')), $pipes); if (is_resource($proc)) { fwrite($pipes[0], "here's your input\n"); fclose($pipes[0]); echo stream_get_contents($pipes[1]); fclose($pipes[1]); $result_code = proc_close($proc); echo "$result_code\n"; } // ----------------------------- $all = array(); $outlines = array(); $errlines = array(); exec("( $cmd | sed -e 's/^/stdout: /' ) 2>&1", $all); foreach ($all as $line) { $pos = strpos($line, 'stdout: '); if ($pos !== false && $pos == 0) { $outlines[] = substr($line, 8); } else { $errlines[] = $line; } } print("STDOUT:\n"); print_r($outlines); print("\n"); print("STDERR:\n"); print_r($errlines); print("\n"); |
$proc = proc_open($cmd, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); if (is_resource($proc)) { // give end of file to kid, or feed him fclose($pipes[0]); // read till EOF $outlines = array(); while (!feof($pipes[1])) { $line = fgets($pipes[1]); if ($line === false) break; $outlines[] = rtrim($line); } // XXX: block potential if massive $errlines = array(); while (!feof($pipes[2])) { $line = fgets($pipes[2]); if ($line === false) break; $errlines[] = rtrim($line); } fclose($pipes[1]); fclose($pipes[2]); proc_close($proc); print("STDOUT:\n"); print_r($outlines); print("\n"); print("STDERR:\n"); print_r($errlines); print("\n"); } // ----------------------------- // cmd3sel - control all three of kids in, out, and error. $cmd = "grep vt33 /none/such - /etc/termcap"; $proc = proc_open($cmd, array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); if (is_resource($proc)) { fwrite($pipes[0], "This line has a vt33 lurking in it\n"); fclose($pipes[0]); $readers = array($pipes[1], $pipes[2]); while (stream_select($read=$readers, $write=null, $except=null, 0, 200000) > 0) { foreach ($read as $stream) { $line = fgets($stream); if ($line !== false) { if ($stream === $pipes[1]) { print "STDOUT: $line"; } else { print "STDERR: $line"; } } if (feof($stream)) { $readers = array_diff($readers, array($stream)); } } } fclose($pipes[1]); fclose($pipes[2]); proc_close($proc); } |
// PHP supports fork/exec/wait but not pipe. However, it does // support socketpair, which can do everything pipes can as well // as bidirectional communication. The original recipes have been // modified here to use socketpair only. // ----------------------------- // pipe1 - use socketpair and fork so parent can send to child $sockets = array(); if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { die(socket_strerror(socket_last_error())); } list($reader, $writer) = $sockets; $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { socket_close($reader); $line = sprintf("Parent Pid %d is sending this\n", getmypid()); if (!socket_write($writer, $line, strlen($line))) { socket_close($writer); die(socket_strerror(socket_last_error())); } socket_close($writer); pcntl_waitpid($pid, $status); } else { socket_close($writer); $line = socket_read($reader, 1024, PHP_NORMAL_READ); printf("Child Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); socket_close($reader); // this will happen anyway exit(0); } // ----------------------------- // pipe2 - use socketpair and fork so child can send to parent $sockets = array(); if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { die(socket_strerror(socket_last_error())); } list($reader, $writer) = $sockets; $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { socket_close($writer); $line = socket_read($reader, 1024, PHP_NORMAL_READ); printf("Parent Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); socket_close($reader); pcntl_waitpid($pid, $status); } else { socket_close($reader); $line = sprintf("Child Pid %d is sending this\n", getmypid()); if (!socket_write($writer, $line, strlen($line))) { socket_close($writer); die(socket_strerror(socket_last_error())); } socket_close($writer); // this will happen anyway exit(0); } // ----------------------------- // pipe3 and pipe4 demonstrate the use of perl's "forking open" // feature to reimplement pipe1 and pipe2. pipe5 uses two pipes // to simulate socketpair. Since PHP supports socketpair but not // pipe, and does not have a "forking open" feature, these // examples are skipped here. // ----------------------------- // pipe6 - bidirectional communication using socketpair $sockets = array(); if (!socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets)) { die(socket_strerror(socket_last_error())); } list($child, $parent) = $sockets; $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { socket_close($parent); $line = sprintf("Parent Pid %d is sending this\n", getmypid()); if (!socket_write($child, $line, strlen($line))) { socket_close($child); die(socket_strerror(socket_last_error())); } $line = socket_read($child, 1024, PHP_NORMAL_READ); printf("Parent Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); socket_close($child); pcntl_waitpid($pid, $status); } else { socket_close($child); $line = socket_read($parent, 1024, PHP_NORMAL_READ); printf("Child Pid %d just read this: `%s'\n", getmypid(), rtrim($line)); $line = sprintf("Child Pid %d is sending this\n", getmypid()); if (!socket_write($parent, $line, strlen($line))) { socket_close($parent); die(socket_strerror(socket_last_error())); } socket_close($parent); exit(0); } |
// ----------------------------- // % mkfifo /path/to/named.pipe // ----------------------------- $fifo = fopen('/path/to/named.pipe', 'r'); if ($fifo !== false) { while (!feof($fifo)) { $line = fgets($fifo); if ($line === false) break; echo "Got: $line"; } fclose($fifo); } else { die('could not open fifo for read'); } // ----------------------------- $fifo = fopen('/path/to/named.pipe', 'w'); if ($fifo !== false) { fwrite($fifo, "Smoke this.\n"); fclose($fifo); } else { die('could not open fifo for write'); } // ----------------------------- // % mkfifo ~/.plan # isn't this everywhere yet? // % mknod ~/.plan p # in case you don't have mkfifo // ----------------------------- // dateplan - place current date and time in .plan file while (true) { $home = getenv('HOME'); $fifo = fopen("$home/.plan", 'w'); if ($fifo === false) { die("Couldn't open $home/.plan for writing.\n"); } fwrite($fifo, 'The current time is ' . strftime('%a, %d %b %Y %H:%M:%S %z') . "\n"); fclose($fifo); sleep(1); } // ----------------------------- // fifolog - read and record log msgs from fifo $fifo = null; declare(ticks = 1); function handle_alarm($signal) { global $fifo; if ($fifo) fclose($fifo); // move on to the next queued process } pcntl_signal(SIGALRM, 'handle_alarm'); while (true) { pcntl_alarm(0); // turn off alarm for blocking open $fifo = fopen('/tmp/log', 'r'); if ($fifo === false) { die("can't open /tmp/log"); } pcntl_alarm(1); // you have 1 second to log $service = fgets($fifo); if ($service === false) continue; // interrupt or nothing logged $service = rtrim($service); $message = fgets($fifo); if ($message === false) continue; // interrupt or nothing logged $message = rtrim($message); pcntl_alarm(0); // turn off alarms for message processing if ($service == 'http') { // ignoring } elseif ($service == 'login') { // log to /var/log/login $log = fopen('/var/log/login', 'a'); if ($log !== false) { fwrite($log, strftime('%a, %d %b %Y %H:%M:%S %z') . " $service $message\n"); fclose($log); } else { trigger_error("Couldn't log $service $message to /var/log/login\n", E_USER_WARNING); } } } |
// sharetest - test shared variables across forks $SHM_KEY = ftok(__FILE__, chr(1)); $handle = sem_get($SHM_KEY); $buffer = shm_attach($handle, 1024); // The original recipe has an INT signal handler here. However, it // causes erratic behavior with PHP, and PHP seems to do the right // thing without it. for ($i = 0; $i < 10; $i++) { $child = pcntl_fork(); if ($child == -1) { die('cannot fork'); } elseif ($child) { $kids[] = $child; // in case we care about their pids } else { squabble(); exit(); } } while (true) { print 'Buffer is ' . shm_get_var($buffer, 1) . "\n"; sleep(1); } die('Not reached'); function squabble() { global $handle; global $buffer; $i = 0; $pid = getmypid(); while (true) { if (preg_match("/^$pid\\b/", shm_get_var($buffer, 1))) continue; sem_acquire($handle); $i++; shm_put_var($buffer, 1, "$pid $i"); sem_release($handle); } } // Buffer is 14357 1 // Buffer is 14355 3 // Buffer is 14355 4 // Buffer is 14354 5 // Buffer is 14353 6 // Buffer is 14351 8 // Buffer is 14351 9 // Buffer is 14350 10 // Buffer is 14348 11 // Buffer is 14348 12 // Buffer is 14357 10 // Buffer is 14357 11 // Buffer is 14355 13 // ... |
// Available signal constants % php -r 'print_r(get_defined_constants());' | grep '\[SIG' | grep -v _ [SIGHUP] => 1 [SIGINT] => 2 [SIGQUIT] => 3 [SIGILL] => 4 [SIGTRAP] => 5 [SIGABRT] => 6 [SIGIOT] => 6 [SIGBUS] => 7 [SIGFPE] => 8 [SIGKILL] => 9 [SIGUSR1] => 10 [SIGSEGV] => 11 [SIGUSR2] => 12 [SIGPIPE] => 13 [SIGALRM] => 14 [SIGTERM] => 15 [SIGSTKFLT] => 16 [SIGCLD] => 17 [SIGCHLD] => 17 [SIGCONT] => 18 [SIGSTOP] => 19 [SIGTSTP] => 20 [SIGTTIN] => 21 [SIGTTOU] => 22 [SIGURG] => 23 [SIGXCPU] => 24 [SIGXFSZ] => 25 [SIGVTALRM] => 26 [SIGPROF] => 27 [SIGWINCH] => 28 [SIGPOLL] => 29 [SIGIO] => 29 [SIGPWR] => 30 [SIGSYS] => 31 [SIGBABY] => 31 // Predefined signal handler constants % php -r 'print_r(get_defined_constants());' | grep '\[SIG' | grep _ [SIG_IGN] => 1 [SIG_DFL] => 0 [SIG_ERR] => -1 |
// send pid a signal 9 posix_kill($pid, 9); // send whole job a signal 1 posix_kill($pgrp, -1); // send myself a SIGUSR1 posix_kill(getmypid(), SIGUSR1); // send a SIGHUP to processes in pids foreach ($pids as $pid) posix_kill($pid, SIGHUP); // ----------------------------- // Use kill with pseudo-signal 0 to see if process is alive. if (posix_kill($minion, 0)) { echo "$minion is alive!\n"; } else { echo "$minion is deceased.\n"; } |
// call got_sig_quit for every SIGQUIT pcntl_signal(SIGQUIT, 'got_sig_quit'); // call got_sig_pipe for every SIGPIPE pcntl_signal(SIGPIPE, 'got_sig_pipe'); // increment ouch for every SIGINT function got_sig_int($signal) { global $ouch; $ouch++; } pcntl_signal(SIGINT, 'got_sig_int'); // ignore the signal INT pcntl_signal(SIGINT, SIG_IGN); // restore default STOP signal handling pcntl_signal(SIGSTOP, SIG_DFL); |
// the signal handler function ding($signal) { fwrite(STDERR, "\x07Enter your name!\n"); } // prompt for name, overriding SIGINT function get_name() { declare(ticks = 1); pcntl_signal(SIGINT, 'ding'); echo "Kindly Stranger, please enter your name: "; while (!@stream_select($read=array(STDIN), $write=null, $except=null, 1)) { // allow signals to be observed } $name = fgets(STDIN); // Since pcntl_signal() doesn't return the old signal handler, the // best we can do here is set it back to the default behavior. pcntl_signal(SIGINT, SIG_DFL); return $name; } |
function got_int($signal) { pcntl_signal(SIGINT, 'got_int'); // but not for SIGCHLD! // ... } pcntl_signal(SIGINT, 'got_int'); // ----------------------------- declare(ticks = 1); $interrupted = false; function got_int($signal) { global $interrupted; $interrupted = true; // The third argument to pcntl_signal() determines if system calls // should be restarted after a signal. It defaults to true. pcntl_signal(SIGINT, 'got_int', false); // or SIG_IGN } pcntl_signal(SIGINT, 'got_int', false); // ... long-running code that you don't want to restart if ($interrupted) { // deal with the signal } |
// ignore signal INT pcntl_signal(SIGINT, SIG_IGN); // install signal handler declare(ticks = 1); function tsktsk($signal) { fwrite(STDERR, "\x07The long habit of living indisposeth us for dying."); pcntl_signal(SIGINT, 'tsktsk'); } pcntl_signal(SIGINT, 'tsktsk'); |
pcntl_signal(SIGCHLD, SIG_IGN); // ----------------------------- declare(ticks = 1); function reaper($signal) { $pid = pcntl_waitpid(-1, $status, WNOHANG); if ($pid > 0) { // ... reaper($signal); } // install *after* calling waitpid pcntl_signal(SIGCHLD, 'reaper'); } pcntl_signal(SIGCHLD, 'reaper'); // ----------------------------- declare(ticks = 1); function reaper($signal) { $pid = pcntl_waitpid(-1, $status, WNOHANG); if ($pid == -1) { // No child waiting. Ignore it. } else { if (pcntl_wifexited($signal)) { echo "Process $pid exited.\n"; } else { echo "False alarm on $pid\n"; } reaper($signal); } pcntl_signal(SIGCHLD, 'reaper'); } pcntl_signal(SIGCHLD, 'reaper'); |
// PHP does not support sigprocmask().
|
declare(ticks = 1); $aborted = false; function handle_alarm($signal) { global $aborted; $aborted = true; } pcntl_signal(SIGALRM, 'handle_alarm'); pcntl_alarm(3600); // long-time operations here pcntl_alarm(0); if ($aborted) { // timed out - do what you will here } |