I've been wrestling with exec(), trying to capture the output from it when I add a task using the at Unix system command. My problem is that it is giving no output when run from my script, however running it from the terminal and PHP in interactive mode prints out a couple of lines.
The command I want to execute is this:
echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
var_dump() gives string(0) "", and print_r() spits out Array (). I've tried using shell_exec(), which outputs NULL, however the following outputs hi when run in a web page context:
echo exec("echo 'hi'");
This also outputs stuff:
echo exec("atq");
However, as soon as I use at, nothing is output. How can I get the output of:
exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
Because at present it outputs nothing when run as "normal" by PHP through Apache, however running the command in the terminal as well as in PHP's interactive console gives me the expected result of something like:
php > echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
warning: commands will be executed using /bin/sh
job 1219 at Sun Jun 10 12:43:00 2012
safe_mode is off, and I cannot work out why I don't get any output from at with a piped-in echo statement, when executing atq or any other commend with exec() gives me output. I've searched and read this question, all to no avail.
How can I get exec() to return the output from at to either a string, or an array if using a second argument with exec()?
Working, one line solution
I didn't realise it could be this simple. All that is required is to reroute stderr to stdout by putting 2>&1 at the end of the command to execute. Now any output from at is printed to stdout, therefore captured by exec():
echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes 2>&1", $result);
My old solution:
I was trying to keep to a one/two line solution, however the only thing that worked in the end was using proc_open() because at logs to stderr, which exec() doesn't read! I'd like to thank #Tourniquet for pointing this out, however he has deleted his answer. To quote:
As far as i can see, at outputs to stderr, which isn't captured by
exec. I'm not really confident in it, but consider using
http://php.net/manual/en/function.proc-open.php, which allows you to
direct stderr to its own pipe.
This is actually the correct way of doing things. My solution (because I only want stderr) was to do this:
// Open process to run `at` command
$process = proc_open("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", array(2 => array("pipe", "w")), $pipes);
// Get stuff from stderr, because `at` prints out there for some odd reason
if(is_resource($process)) {
$output = stream_get_contents($pipes[2], 100);
fclose($pipes[2]);
$return_value = proc_close($process);
}
$output now contains whatever at printed to stderr (which should really go to stdout because it's not an error), and $return_value contains 0 on success.
Here a more complex solution with proc_open. I'm writing this answer because, in my case, the '2>&1' workaround doesn't work.
function runCommand($bin, $command = '', $force = true)
{
$stream = null;
$bin .= $force ? ' 2>&1' : '';
$descriptorSpec = array
(
0 => array('pipe', 'r'),
1 => array('pipe', 'w')
);
$process = proc_open($bin, $descriptorSpec, $pipes);
if (is_resource($process))
{
fwrite($pipes[0], $command);
fclose($pipes[0]);
$stream = stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
}
return $stream;
}
Usage examples:
// getting the mime type of a supplied file
echo runCommand('file -bi ' . escapeshellarg($file));
Another example using the command parameter:
// executing php code on the fly
echo runCommand('/usr/bin/php', '<?php echo "hello world!"; ?>');
Another example using the force parameter (this can be useful for commands that will change the output during the execution process):
// converting an mp3 to a wav file
echo runCommand('lame --decode ' . escapeshellarg($source) . ' ' . escapeshellarg($dest), '', true);
I hope this helps :-)
Try to create a minimum (non-)working example. Break everything down, and test only one thing at a time.
Here is one error in your bash:
hpek#melda:~/temp$ echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes
at: pluralization is wrong
job 22 at Sun Jun 10 14:48:00 2012
hpek#melda:~/temp$
Write minute instead og minutes.
My output from at is send to me by mail!!
Related
In my test.php script I have this:
$out = exec ( 'ps -ef' );
echo $out;
Which outputs just this when I run "php test.php":
root 16682 2 0 Jan30 ? 00:00:00 [NFSv4 callback]
However, when I run "ps -ef" from the command line I get the usual long list of processes for all users..
Any ideas why the php script produces such different results?
Please try
shell_exec ( 'ps -ef' );
This will return the entire output, whereas exec returns the last line of output:
http://php.net/manual/en/function.exec.php
I have the following code in place. It provides the information needed, however I would like to assign the output to variables.
$cmd = "ssh machine 'cat /usr/local/reports/file.dat | awk -F'[[:space:]][[:space:]][[:space:]]*' '{print \"<tr><td>\"$2\"</td><td>\"$3\"</td></tr>\"}'";
system($cmd);
This correctly runs and produces a table with the 2nd and 3rd columns from the file. However, I would now like to assign the columns to variables for each line read in the file.
Any ideas?
system always outputs the command output directly. You could use output buffering to capture it, but you should use shell_exec instead:
$result = shell_exec( $cmd );
Few suggestions:
Use heredoc to make reader friendly
avoid cat /usr/local/reports/file, awk can read file directly, there is no need of using cat command
if you want take care of return status use exec() function.
shell_exec() returns all of the output stream as a string. exec returns the last line of the output by default, but can provide all output as an array specifed as the second parameter.
Here is code snippet
<?php
$cmd =<<<EOF
ssh user#host "awk -F'[[:space:]][[:space:]][[:space:]]*' '{
print \"<tr><td>\" $2 \"</td><td>\" $3 \"</td></tr>\"
}
' /usr/local/reports/file.dat 2>&1"
EOF;
/*
execute command in 1st argument,
save output in array in 2nd argument
store status in 3rd argument
*/
exec($cmd, $out, $return);
if($return==0)
{
print_r($out);
/* your case you can just
echo implode(PHP_EOL, $out);
*/
}else{
/* Failed to execute command
do some error handling */
die( 'Failed to execute command : '. $cmd );
}
?>
This question already has answers here:
PHP reading shell_exec live output
(3 answers)
Closed 6 years ago.
I have to execute a batch file (I.e. .bat ) and get the result on an HTML page.
I used this PHP code to get a result:
<?php
echo "<br>";
$result = shell_exec('start file.bat');
iconv("CP850","UTF-8",$result);
echo "<pre>$result </pre>";
?>
Now the problem is that I get a result only when the batch file execution finishes, and I want to have the result in real time, like running via command line.
Found in the comments of the shell_exec documentation's page of PHP
If you're trying to run a command such as "gunzip -t" in shell_exec and getting an empty result, you might need to add 2>&1 to the end of the command, eg:
Won't always work:
echo shell_exec("gunzip -c -t $path_to_backup_file");
Should work:
echo shell_exec("gunzip -c -t $path_to_backup_file 2>&1");
In the above example, a line break at the beginning of the gunzip output seemed to prevent shell_exec printing anything else. Hope this saves someone else an hour or two.
I believe it is what you really have to do.
Reference: http://php.net/manual/en/function.shell-exec.php#106250
As already mentioned in the comments to your question you need to fork a process in such way that you can communicate with it. That is not possible with imple functions like exec() and the like.
Instead take a look at this simple example:
File test.sh:
#!/bin/bash
echo start counting ...
for counter in 1 2 3
do
echo * counter is at value $counter *
sleep 3
done
echo ... finished counting.
File test.php:
<?php
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
);
echo "forking process ...\n";
$process = proc_open('./test.sh', $descriptorspec, $pipes);
if (is_resource($process)) {
echo "... successfully forked process ...\n";
while (!feof($pipes[1])) {
echo fread($pipes[1], 1024);
flush();
}
echo "... process has finished ...\n";
fclose($pipes[0]);
fclose($pipes[1]);
echo "... pipes closed ...\n";
proc_close($process);
echo "... process closed.\n";
} else {
echo "... failed to fork process!\n";
}
The obvious output of a test run of that php script is:
forking process ...
... successfully forked process ...
start counting ...
* counter is at value 1 *
* counter is at value 2 *
* counter is at value 3 *
... finished counting.
... process has finished ...
... pipes closed ...
... process closed.
But the interesting part here is that this output is not sent in one go once that forked process has finished, but in what you referred to as "a live manner". So the first four lines appear immediately, the next two with a 3 second delay each, then the rest of the output.
Please note that the above example is meant as a demonstration for a Linux CLI environment. So it does not care about html markup but outputs plain text and relies on bash as a shell environment for the forked process. You will have to adapt that simple demonstration to your needs, obviously.
I have a tty device (/dev/ttyUSB0), which occasionally outputs a string in the form of Cycle 1: 30662 ms, 117.41 W. I'm using a simple bash script to process it:
#!/bin/sh
stty -F /dev/ttyUSB0 57600
cd /home/pi
while true; do
cat /dev/ttyUSB0 | awk '{ print $0 > "/dev/stderr"; if (/^Cycle/) { print "update kWh.rrd N:" $5 } }' | php5 test.php
sleep 1
done
The test.php script looks like this:
<?php
stream_set_blocking(STDIN, 0);
$line = trim(fgets(STDIN));
$file = 'kwhoutput.txt';
$current = file_get_contents($file);
$current .= $line;
file_put_contents($file, $current);
?>
however, the kwhoutput.txt remains empty. Why is this not working?
awk is buffering your data. Use fflush() to flush the buffers after each output line:
awk '{
print $0 > "/dev/stderr";
if (/^Cycle/) {
print "update kWh.rrd N:" $5;
fflush();
}
}' < /dev/ttyUSB0 | php5 test.php
Also make sure that /dev/ttyUSB0 actually outputs a line (terminated by \n), and not just a string of data.
You should also fix up your php script to:
Read multiple lines and append them one by one (otherwise, the script will skip every other line).
Find out how to append to a file in php. Reading the whole file, concatenating a string in memory, then writing the whole file is not the way to go.
I was trying to execute this command
echo exec("top");
and
echo exec("/usr/bin/top");
neither works (returns blank output)
does anybody know why?
Because top is an interactive program that is meant to be run on a terminal, not be executed from a script. You are probably want to run the 'ps' command with arguments which will sort output by cpu utilization.
http://www.devdaily.com/linux/unix-linux-process-memory-sort-ps-command-cpu
You actually can call top and echo its output. Code that worked for me:
passthru('/usr/bin/top -b -n 1');
-b - running in batch mode
-n 1 - only one iteration
It probably works, but exec() doesn't return anything. Read the Manual: exec()
$output = null;
exec('top', $output);
echo $output;
But you have another problem: top doesn't exit by itself. You cannot use it here, because you need to send the interrupt-signal (just realized: q is ok too).
One solution is to make top to stop after one iteration
$output = null;
exec('top -n 1', $output);
var_dump($output);
If you want to put it in a variable :
ob_start();
passthru('/usr/bin/top -b -n 1');
$output = ob_get_clean();
ob_clean();
I used:
$cpu = preg_split('/[\s]+/', shell_exec('mpstat 1 1'));
$cpu = 100-$cpu[42];
100% minus the idle time.