If you want to execute a command, and get both stderr and stdout, not "merged", a solution would probably to use proc_open, which provides a great level of control over the command that's being executed -- including a way to pipe stdin/stdout/stderr.
And here is an example : let's consider we have this shell-script, in test.sh, which writes to both stderr and stdout :
#!/bin/bash
echo 'this is on stdout';
echo 'this is on stdout too';
echo 'this is on stderr' >&2;
echo 'this is on stderr too' >&2;
Now, let's code some PHP, in temp.php -- first, we initialize the i/o descriptors :
$descriptorspec = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"], // stdout
2 => ["pipe", "w"], // stderr
];
And, then, execute the test.sh command, using those descriptors, in the current directory, and saying the i/o should be from/to $pipes :
$process = proc_open('./test.sh', $descriptorspec, $pipes, dirname(__FILE__), null);
We can now read from the two output pipes :
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
And, if we output the content of those two variables :
echo "stdout : \n";
var_dump($stdout);
echo "stderr :\n";
var_dump($stderr);
We get the following output when executing the temp.php script :
$ php ./temp.php
stdout :
string(40) "this is on stdout
this is on stdout too
"
stderr :
string(40) "this is on stderr
this is on stderr too
"
Answer from Pascal MARTIN on Stack OverflowIf you want to execute a command, and get both stderr and stdout, not "merged", a solution would probably to use proc_open, which provides a great level of control over the command that's being executed -- including a way to pipe stdin/stdout/stderr.
And here is an example : let's consider we have this shell-script, in test.sh, which writes to both stderr and stdout :
#!/bin/bash
echo 'this is on stdout';
echo 'this is on stdout too';
echo 'this is on stderr' >&2;
echo 'this is on stderr too' >&2;
Now, let's code some PHP, in temp.php -- first, we initialize the i/o descriptors :
$descriptorspec = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"], // stdout
2 => ["pipe", "w"], // stderr
];
And, then, execute the test.sh command, using those descriptors, in the current directory, and saying the i/o should be from/to $pipes :
$process = proc_open('./test.sh', $descriptorspec, $pipes, dirname(__FILE__), null);
We can now read from the two output pipes :
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
And, if we output the content of those two variables :
echo "stdout : \n";
var_dump($stdout);
echo "stderr :\n";
var_dump($stderr);
We get the following output when executing the temp.php script :
$ php ./temp.php
stdout :
string(40) "this is on stdout
this is on stdout too
"
stderr :
string(40) "this is on stderr
this is on stderr too
"
A little function that might be helpful:
function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {
$proc = proc_open($cmd,[
1 => ['pipe','w'],
2 => ['pipe','w'],
],$pipes);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
return proc_close($proc);
}
The exit code is returned and STDOUT and STDERR are reference params if you need them.
PHP - How to get Shell errors echoed out to screen - Stack Overflow
php - getting output and exit status from shell_exec() - Stack Overflow
Executing a shell command from PHP with shell_exec - Unix & Linux Stack Exchange
shell - PHP shell_exec() vs exec() - Stack Overflow
As you've already seen, when using shell_exec you have to chain your "real" command with echo $? to get the exit status:
$output_including_status = shell_exec("command 2>&1; echo $?");
but if you want the clean way, then you want to use the exec function, which allows a 3rd agument explicitly for this purpose.
Following worked for me with exec() to show the output
exec(your_command, $output, $return_var);
var_dump($output);
var_dump($return_var);
On the php manual for shell_exec, it shows that the function returns the output as a string. If you expect output from the program you launch, you need to capture this like so:
$execQuery = "echo -n test_command";
$output = shell_exec($execQuery);
echo $output;
Your question doesn't show trying to capture any data. If you also make sure to connect stdout and stderr when you run your command, you should get a better idea is what is going on. To use your example:
$output = shell_exec("/usr/bin/oneuser create test10 test10 2>&1");
var_dump($output);
That should help you see what is going on. As Shadur suggests, it seems likely that these programs expect an interactive terminal that can enter passwords in order to run. Even if don't need input, they might expect interactive shells. And he's right that su doesn't play nice in this context. There is, however, a correct tool for the job.
You can setup sudo to such that your http user can execute your program as username without a password but NOT be able to do anything else by running visudo or whatever you use to edit your sudoers file and adding this line:
http ALL=(username) /usr/bin/oneadmin
Then in php your command would look something like this:
$execQuery = "sudo -u username /usr/bin/oneadmin postgres -c '/usr/bin/oneuser create test10 test10'";
$out = shell_exec ("$execQuery 2>&1");
echo $out
Try passthru($cmd);
It will allow user's I/O on the Terminal screen.
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.
See
- http://php.net/manual/en/function.shell-exec.php
- http://php.net/manual/en/function.exec.php
Here are the differences. Note the newlines at the end.
> shell_exec('date')
string(29) "Wed Mar 6 14:18:08 PST 2013\n"
> exec('date')
string(28) "Wed Mar 6 14:18:12 PST 2013"
> shell_exec('whoami')
string(9) "mark\n"
> exec('whoami')
string(8) "mark"
> shell_exec('ifconfig')
string(1244) "eth0 Link encap:Ethernet HWaddr 10:bf:44:44:22:33 \n inet addr:192.168.0.90 Bcast:192.168.0.255 Mask:255.255.255.0\n inet6 addr: fe80::12bf:ffff:eeee:2222/64 Scope:Link\n UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1\n RX packets:16264200 errors:0 dropped:1 overruns:0 frame:0\n TX packets:7205647 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:1000 \n RX bytes:13151177627 (13.1 GB) TX bytes:2779457335 (2.7 GB)\n"...
> exec('ifconfig')
string(0) ""
Note that use of the backtick operator is identical to shell_exec().
Update: I really should explain that last one. Looking at this answer years later even I don't know why that came out blank! Daniel explains it above -- it's because exec only returns the last line, and ifconfig's last line happens to be blank.
Update and answer for the benefit of anyone who comes here with a similar issue.
After spending a lot of time on this I gave up with exec() and gave proc_open() a try instead.
Doing it this way now works locally and on the server:
<?php
$cmd = "__test.bat";
$descriptorspec = array(
0 => array("pipe", "r"), // STDIN
1 => array("pipe", "w"), // STDOUT
2 => array("pipe", "w"), // STDERR
);
$cwd = getcwd();
$env = null;
$proc = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);
if (is_resource($proc)) {
// Output test:
echo "STDOUT:<br />";
echo "<pre>".stream_get_contents($pipes[1])."</pre>";
echo "STDERR:<br />";
echo "<pre>".stream_get_contents($pipes[2])."</pre>";
$return_value = proc_close($proc);
echo "Exited with status: {$return_value}";
}
?>
For some reason, missing out the getcwd() part causes the command to fail on the server unless I specify the complete path, whereas locally that is not an issue.
With this method I can append 2>&1 to redirect all output to STDIN. To output to a file, the manual shows that the $descriptorspec array can be modified e.g: 2 => array("file", "stderr.log", "a") (I have not yet tested this though)
One difference here is that if I want to retrieve the output in PHP, rather than getting all of the lines in an array, I need to read from the streams using stream_get_contents().
I still don't understand why there was an issue with using exec(), but this method seems to work both locally and on the server - If anyone knows why this could be, please let me know!
$o = null;
$r = null;
exec("php test.php 2>&1", $o, $r );
It's quite simple