One of my users is experiencing odd behavior that doesn't seem to follow the PHP exec() specs.
I'm invoking like so:
exec($cmd, $out, $ret);
I would expect this not to generate any output, but the user is seeing an error stack trace from $cmd printed when it errors out. Nothing in my code outputs anything, so it has to be coming out of exec(), but how? Am I misinterpreting the documentation?
I expect I could trap this inside of an output buffer then dispose of it, but I would rather prevent it... And whatever I do, I want to understand why this is happening first.
NOTE: I'm working on getting more specifics on PHP version and other d etails from the users, but do not have that information at this time. All I can say is that it is >= 5.2.4.
What you experience is the normal unix behaviour:
An executed command always has two output pipes: standard out and error out. The documentation states that all output is given back. That does not include stuff written to the error output. That is using a separate pipe py purpose to separate error and normal output. If you want to capture that too you have to change the command you execute, typically you map the error output to the standard output by appending a 2>&1.
Related
I just cannot fathom how to get the PHP exec() or shell_exec() functions to treat a '*' character as a wildcard. Is there some way to properly encode / escape this character so it makes it through to the shell?
This is on windows (via CLI shell script if that matters, Terminal or a git-bash yields the same results).
Take the following scenario:
C:\temp\ contains a bunch of png images.
echo exec('ls C:\temp\*');
// output: ls: cannot access 'C:\temp\*': No such file or directory
Permissions is not the problem:
echo exec('ls C:\temp\exmaple.png');
// output: C:\temp\example.png
Therefore the * character is the problem and is being treated as a literal filename rather than a wildcard. The file named * does not exist, so from that point of view, it's not wrong...
It also does not matter if I use double quotes to encase the command:
echo exec("ls C:\temp\*");
// output: ls: cannot access 'C:\temp\*': No such file or directory
I have also tried other things like:
exec(escapeshellcmd('ls C:\temp\*'));
exec('ls C:\temp\\\*');
exec('ls "C:\temp\*"');
exec('ls "C:\temp\"*');
And nothing works...
I'm pretty confused that I cannot find any other posts discussing this but maybe I'm just missing it. At this point I have already worked around the issue by manually programming a glob loop and using the internal copy() function on each file individually, but it's really bugging me that I do not understand how to make the wildcard work via shell command.
EDIT:
Thanks to #0stone0 - The answer provided did not particularly answer my initial question but I had not tried using forward slashes in the path and when I do:
exec('ls C:/temp/*')
It works correctly, and as 0stone0 said, it only returns the last line of the output, which is fine since this was just for proof of concept as I was not actually attempting to parse the output.
Also, on a side note, since posting this question my system had been updated to Win11 22H2 and now for some reason the original test code (with the backslashes) no longer returns the "Cannot access / no file" error message. Instead it just returns an empty string and has no output set to the &$output parameter either. That being said, I'm not sure if the forward slashes would have worked on my system prior to the 22H2 update.
exec() only returns the last output line by default.
The wildcard probably works, but the output is just truncated.
Pass an variable by ref to exec() and log that:
<?php
$output = [];
exec('ls -lta /tmp/*', $output);
var_dump($output);
Without any additional changes, this returns the same as when I run ls -lta /tmp/* in my Bash terminal
That said, glob() is still the preferred way of getting data like this especcially since
You shouldn't parse the output of ls
Suddenly, an application isn't any longer able to output ZIP files. An inspection revealed the cause: The first character of the ZIP is a blank, which breaks the ZIP format spec.
To track down this problem, I enabled CStatementTracer, which prints each line of executed code to a log file. Didn't help. [Remark: declare(ticks=1); doesn't seem to trap each line of executed code]
I then set an output handler like so:
function callback( $buffer ) {
$deb = print_r( debug_backtrace(), TRUE );
file_put_contents( './statementTrager.log', $deb );
return $buffer;
}
ob_start("callback", 1 );
Unfortunately, this handler isn't called at all.
Q: Does a generic / canonical solution exists, which identifies the file / line of PHP-code, which emits the first character.
A solution, that finds the loc whatever other code gets executed.
Remarks:
Not even a single PHP file is closed using ?>
Meanwhile I found the suspicious like of code: A blank in front of a starting
Still, I'd like to get hints regarding a programmatic solution. Preferrably a solution written in pure PHP.
https://linux.die.net/man/1/strace is probably the most reliable tool to find out where the output comes from. Assuming you are on Linux. There must be similar tools for other platforms.
Although it will not give you the line of the php code, you can analyse the context of system calls made before and after the offensive character was sent. Usually it is enough to identify where the problem originates.
It is quite time consuming process though. Should be used as the last resort.
This is my code for executing a command from PHP:
$execQuery = sprintf("/usr/local/bin/binary -mode M \"%s\" %u %s -pathJson \"/home/ec2/fashion/jsonS/\" -pathJson2 \"/home/ec2/fashion/jsonS2/\"", $path, $pieces, $type);
exec($execQuery, $output, $return);
the $return value is always 0 but $output is empty. The $output should be a JSON.
If I execute the same but removing one letter to binary (for example /usr/local/bin/binar ) I get (correctly) a $return = 127.
If I write other parameters (like -mode R which doesn't exit) I got errors from the console (which are correct as well).
If I run the exact $execQuery (which I printf before to be sure about quotation marks) on the console, it executes correctly. It's only the PHP side where I've got the error.
What can be wrong?
Thank you in advance.
Well, a couple of things might be happening...
This binary you're running write to something else that STDOUT (for instance, STDERR)
The env vars available to the PHP user differ from the env vars available to the user running console (and those vars are required)
PHP User does not have permission to access some files involved.
In order to debug, it might be better to use proc_open instead of exec, and check the STDOUT and STDERR. This might give you additional information regarding what's happening.
Suggestion (and shameless advertising)
I wrote a small utility library for PHP that executes external programs in a safer way and provides aditional debug information. It might help you to, at least pinpoint the issue.
I have a great Python program on my webserver, which I want to use from inside my PHP web app.
Here's an example of the python command, and output as you would see it in terminal:
>>> print MBSP.parse('I ate pizza with a fork.')
I/PRP/I-NP/O/NP-SBJ-1/O/i
ate/VBD/I-VP/O/VP-1/A1/eat
pizza/NN/I-NP/O/NP-OBJ-1/O/pizza
with/IN/I-PP/B-PNP/O/P1/with
a/DT/I-NP/I-PNP/O/P1/a
fork/NN/I-NP/I-PNP/O/P1/fork ././O/O/O/O/.
You might recognize this as a typical POS tagger.
In any case, I'm confused about how to use a PHP-based web app to send this program a string like "I ate pizza with a fork", and somehow get the response back in a way that can be further parsed in PHP.
The idea is to use PHP to pass this text to the Python program, and then grab the response to be parsed by PHP by selecting certain types of words.
It seems like in PHP the usual suspects are popen() and proc_open(), but popen() is only for sending, or receiving information - not both? Is popen() able to give me access to this output (above) that I'm getting from the Python program? Or is there a better method? What about curl?
Here are all my options in terms of functions in PHP:
http://us.php.net/manual/en/function.proc-open.php
I'm lost on this, so thanks for your wise words of wisdom!
I use exec() for this purpose.
exec($command, $output);
print_r($output);
If you want to get a little heavier / fancier... give your python script an http (or xmlrpc) front end, and call that with a GET/POST. Might not be worth all that machinery though!
You could use popen(), and pass the input to your Python script as a command line argument, then read the output from the file descriptor popen gives you, or proc_open() if you want to interact bi-directionally with the Python script.
Example 1 in the proc_open manual: http://us.php.net/manual/en/function.proc-open.php gives an example of this.
If your Python needs it as stdin, you could try popening a command line:
echo "I ate pizza!"|my_python_progam.py
and just read the output. As usual, do proper input validation before sending it to the command-line.
Something like this would work
$command = '/usr/bin/python2.7 /home/a4337/Desktop/script.py'
$pid = popen('$command',r)
........
........
.........
pclose($pid)
i've got a php script that runs on the command line. To improve it's performance, i would like to completely suppress it's output.
How can I do that?
Thanks!
Not sure about performance but you can use
php -q myscript.php > /dev/null
One way would be to call ob_start() to turn on output buffering, and periodically call ob_clean() if you really don't want the output (or use a dummy ob_start() callback which does nothing).
Not sure how much of a performance gain this would be, as internally, PHP is still processing the output, except it goes into a buffer rather than to stdout.
If you've got a lot of diagnostic output you want to prevent, than it might be more effective to refactor your code to allow that to be suppressed by an application option.
fclose(STDOUT);
This will work cross platform as the first line in the PHP script.
Using > /dev/null is not enough?
If you have access to the script replace all echo with //echo. Perfect performance increase, but you will need to check multi-line echo statements.
function ob_esity($b) { return ''; }
ob_start('ob_esity');