I have a python script. When I run the script from the command line as:
python upgrade.py arg1 arg2
The script runs successfully.
Now I am trying to pass the same variables via PHP code using:
passthru("python upgrade_device.py $arg1 $arg2");
The script does not execute.
I am accepting the arguments using:
sys.argv
The arguments are passed correctly
I have tested using print
This is specific command where the execution fails:
child=pexpect.spawn('ssh admin#%s'%ip_addr)
ip_addr is one of the arguments passed from PHP. Right below this command I am ip_addr so as to find what value is passed to the command. and the value is as expected.
I have the ip_addr variable stored correctly.
I have even type casted it to string so that should not be problem.
In general, is there a difference of format in passing variables from the command line and passing from PHP
For my understanding i think passing it through shell and php is the same, i can't be sure for 100% but that what i know from experience.
For your case if you have verified with print that it's the right string to pass (mean take the output and test it in the terminal if it work) if it work, than verify if the python script file is in the same directory as the php script file, because the shell will be opened at the current php script path. And so the shell command should respect that. For example let assume php script file is in the root "/", and python script file is in "/myPythonScripts/imageProccessing/" then when you execute something like "python myPyScript.py 646545645" for example, in this example in php you should execut "python ./myPythonScripts/imageProccessing/myPyScript.py 646545645", as i said the shell is launched on the php script path.
Now if that's not the problem, then try escaping the string first, with escapeshellcmd(). And see what it give. There is characters that are not supported by the shell, or that have a certain signification and parsed by the shell differently. make sure none of them is passed as argument.
In case none of the above is helpful try to use execshell() instead, and see what it give. Too probable that will not make a difference.
Edit: In case nothing helped, try to change just a bit the way of the exchange is done, and use the way it's done in this response using json https://stackoverflow.com/a/14048046/7668448 (it's a kind of more convenient)
Please if any of that solved the problem, precise in a response which one was! I hope it will help. Also if it's not your case it may be useful for other persons in the future. Think also about giving the solution when you do! (i also personally interested too)
Related
I'd use the following code:
$SERVER_PATH = dirname(__FILE__);
shell_exec($PHP_LOCATION.' '.$SERVER_PATH."/script.php?k1=v1&k2=v2 > /dev/null 2>/dev/null &");
Where:
$PHP_LOCATION should contain the path to PHP,
$SERVER_PATH - is current working directory (fortunately the script to run is in the same directory),
> /dev/null 2>/dev/null & added to make this call asynchronous (taken from Asynchronous shell exec in PHP question)
This code has two problems:
As far as I remember ?k1=v1&k2=v2 will work for web-call only, so in this particular case parameters will not be passed to the script.
I don't really know how to init the $PHP_LOCATION variable to be flexible and to work on the most hosts.
I conducted some research regarding both problems:
To solve 1 suggested to use -- 'parameters_string' but it is also recommended to modify the script to parse parameters string which looks a bit clumsy. Is there a better solution?
To solve 2 I found a solution to use PHP_BINARY but this is a PHP 5.4+ case (I'm using 5.3). But the original question was about to run PHP of the same version as the original script version. So for me (as I use PHP 5.3 only) is there probably a solution?
EDIT 0
Let me do some explanation why I stuck to this weird (for PHP) approach:
Those PHP scripts should be separate from each other:
one of those will analyze the data and
the second will generate PNG graphs as a final result.
Those scripts aren't intended to run simultaneously, this means that the second can run at it's own schedule it is only needed that the run should be upon its data will be ready (which done by the first script). So no data should be passed back from second script (child) to the first (parent).
EDIT 1
As seeing from most of the comments the main discussion goes to forking direction. However I'd like to make stress on the point 1 and 2 asked in the original questions. I have some reasons to solve the task in the way I pointed out and I tried to point all that reason. If some of my points looks weird, please post a comment - I will make it more clear or I will change the main question.
Thank you in advance!
How to get executable
Assuming you're using Linux, you can use:
function getBinaryRunner($binary)
{
return trim(shell_exec('which '.$binary));
}
For instance, same may be used for checking if needed stuff is installed:
function checkIfCommandExists($command)
{
$result = shell_exec('which '.$command);
return !empty($result);
}
Some points:
Yes, it will work only for Linux
You should be careful with user input if it is allowed to be passed to shell commands: escapeshellarg() and company
Indeed, normally PHP should not be used for stuff like this as if it is about asynchronous requests, better to either implement forking or run commands from external workers.
How to pass parameters
With doing shell_exec() via file system path you're accessing the file and, obviously, all "GET" parameters are becoming just part of file name, it is no longer "URI" as there is no web-server to process that. So you have two options:
Invoke call via accessing your web-server. So it will be like:
//Yes, you will use wget or, better, curl to make web-request from CLI
shell_exec('wget http://your.web-server.domain/script.php?foo=bar');
Downside here: if you'll access your web-server via public DNS, it will cause network gap and all processing overheads. Benefit - obviously, you will not have to expect anything else in your script and make no distinction between CLI and non-CLI calls
Use $_SERVER array in your script and pass parameters as it should be with CLI:
shell_exec('/usr/bin/php /path/to/script.php foo bar');
//inside your script.php you will see:
//$_SERVER['argv'][0] is "script.php"
//$_SERVER['argv'][1] is "foo"
//$_SERVER['argv'][2] is "bar"
Yes, it will require modification in the script, and, probably, some logic of how to map "regular" web-requests and CLI ones. I would suggest even to think of separating CLI-related stuff to different scripts bundle so not to mess that logic.
More about "asynchronous run"
When you do php script.php & you just run it in background mode. That, however, still keeps parent-child relation for your process. That means - if parent process dies, it's childs will also be removed. To be precise, SIGHUP will be triggered and to avoid this situation you should use nohup command. It will allow to emulate "detaching" of a process and therefore making it's run reliable and independent of circumstances happening to parent process.
I got a program written in C and compiled with gcc, which I've tested for memory leaks etc in both Linux with Valgrind and in Windows with drmemory, and that with the relevant problematic argv arguments. I've found absolutely no problem doing so, and I get the output I expect when doing so. The only libraries included, and barely used are, stdio.h, stdlib.h, string.h, sys/time.h. Only output is printf and fopen to a specific file, which of course is closed after use, no messing with around with stdout or stderr or something like that.
I call this program from PHP with exec, and the program seems to work perfectly almost all the time, except for some few times when the executable takes like 30+ seconds or something along those lines. The problem is then that it seems I don't get the stdout that I should get assigned to the $output argument with the PHP exec function call, neither does it return anything, which should be the last line outputted from the program. The return value argument for the exec function gets set to 0, which really is the only return value the main function in the C program is set to return, there's no exit or anything else. I've of course set the time limit of the PHP script to 0, and it can run for hours just fine. Should also be noted I run this PHP script from cmd.exe in Windows 7 with administrator privileges.
I've been debugging this so much now, and before I decided to post here, I wanted to see if it actually got to the printf part of my program where the output I want should be printed, so I made it write the exact same string to file right after the printf. Guess what? It wrote the expected result to that file......
From this I take it that PHP exec somehow don't get the stdout output after a certain amount of time. Anyone got any ideas on how to fix this?
I guess a possible fix is to make the PHP script check the created file for the output instead of getting it through the $output argument that should get it from stdout. Perhaps do this when it gets nothing from $output, but this seems like a very shabby workaround.
Is there some kind of setting I've missed?
I'm running PHP 5.6.6.
EDIT:
I've also already made sure to set this at the start of the PHP script before I even started doing any of the tests etc:
error_reporting(E_ALL);
set_time_limit(0);
Did you try to extend the maximum execution time in your server configuration (or by using set_time_limit())? The default one is 30 as I remember correctly. Maybe this causes the problems.
I've been fighting with this for a few hours now, and I can't seem to work it out.
tried exec(), shell_exec(), and system(). Nothing works.
I have this:
exec("/usr/bin/php /var/www/vhosts/domain.com/httpdocs/shell/send.php >> /var/www/vhosts/domain.com/httpdocs/shell/paging.log &");
send.php simply has:
echo 'works';
But nothing shows up in the log. I've googled and read stuff on here, but I can't find anything to help.
I'm running php v.5.3.8.
safe mode is on
I'm pretty sure that is the path to php, but can't really find out how to find it, so I'm going on phpinfo().
exec('whoami'); does nothing. Is it suppose to show in the browser? or email you something?
any ideas?
According to PHP Manual for exec function:
When safe mode is enabled, you can only execute files within the
safe_mode_exec_dir. For practical reasons, it is currently not allowed
to have .. components in the path to the executable.
Check http://php.net/manual/en/ini.sect.safe-mode.php#ini.safe-mode-exec-dir
Also, be aware that the web server user must have permission to write in the log file.
EDIT: To turn safe mode off, check not only php.ini file but also virtual hosts specific configurations in your web server, whether it is Apache, NginX or other. If you use Plesk, look in vhosts for httpd.include, and make sure that safe_mode is set to off there as well.
> The last line from the result of the command. If you need to execute
> a command and have all the data from the command passed directly back
> without any interference, use the passthru() function. To get the output
> of the executed command, be sure to set and use the output parameter.
I am not looking for output, and the last two arguments of exec() are optional. I my case what I really need is to be able to open a folder on the desktop. This exact syntax worked very well in MAMP_PRO_1.9.6, but no longer works in MAMP_PRO_2.0.5 (it's broken)
<?php
exec("open /path/to/any/folder"); // BROKEN in Mamp Pro 2.0.5
?>
To get the output, you need to pass a second parameter, or you can get the last line of output by echo'ing it.
From the PHP manual:
string exec ( string $command [, array &$output [, int &$return_var ]] )
Return Values:
The last line from the result of the command. If you need to execute a command and have all the data from the command passed directly back without any interference, use the passthru() function.
To get the output of the executed command, be sure to set and use the output parameter.
I've got a script that calls two functions, A and B, from the same class. A creates an Amazon virtual server and B destroys one, both via shell_exec()'s of Amazon's command line tools. The script, doActions.php, pulls actions from a queue. If the action is "create" it creates an instance; when the action is "destroy" it kills one.
The script works fine to execute both A and B when I execute it from the command line: php script.php.
When I put it on a cron, it runs but only successfully runs the B function. It deletes destroys instances but won't create them.
The point of failure is clearly function B. It chokes at the first and most important shell_exec, returning and echoing nothing.
echo $string = shell_exec('/home/user/public_html/domain.com/private/ec2-api-tools/bin/ec2-run-instances ami-23b6534a -k gsg-keypair -z us-east-1a');
Unless you know something specific about the way Amazon's command line tools work, please suggest to me reasons why a shell_exec might work in one case and not the other.
Another shell_exec in the same place behaves as expected:
echo $string = shell_exec ('echo overflow');
My guess is that it has to do something with permissions. But when I have it run shell_exec('whoami') it return "root," and when I su and run the command it works fine. I'm having a hard time thinking of creative ways to troubleshoot why my PHP script won't work in cron when it does from the command line. Can you suggest some?
When something runs from the command line but refuses to do so within cron, it's often an environment issue (path or some other environment variable that's needed by the code you're running).
For a start you should modify the script to output the current environment (shell_exec('env')?) at the very top and examine the output from the command line and cron.
Hopefully, there will be something obvious such as AMAZON_EC2_VITAL_VAR but, if not, you should move the cron environment towards your command line one, one variable at a time, until it starts working.
A quick test to ascertain this. From your command line, do:
env >/tmp/pax_env.sh
Then run your PHP script from a shell script which first executes:
. /tmp/pax_env.sh
so that the environments are identical.
And keep in mind that su on its own doesn't give you the same environment as you'd get from logging in directly as a specific user (su - does, I think). You may want to check the behaviour for when you log in as root directly.
Re your comment:
Yes, I do believe you've got it. I'm likely going to mark your answer as correct but need you to suffer through a few addendums about your clever solution. First of all, what's the best way to execute the pax_env.sh script? Does shell_exec() work?
Never let it be said I didn't work for my money :-) No. The shell_exec will almost certainly be running a sub-shell so the variables would be set in that sub-shell but would not affect the PHP parent process.
My advice, if you wanted all those variables set, would be to create a shell-script consisting of all the commands in /tmp/pax_env.sh (probably prefixing each with export) followed by the command you currently have running in cron, something along the lines of:
export PATH=.:/usr/bin
export PS1=Urk:
export PS2=MoreUrk:
/home/user/pax/scriptB.php
Then run that script from cron rather than /home/user/pax/scriptB.php directly. That will ensure the environment is set up before your PHP code is called.
Astute readers will have noticed the phrase "if you wanted all those variables set" above. I don't personally think it's a good idea to dump all your command line variables into the shell script for the cron job. I'd prefer to actually find out which ones are needed and only include those. That lessens the pollution your cron job has to run under. For example, it's unlikely that the PS1/PS2 prompt variables will be required for your PHP script.
If it works, you can set all the environment variables - I just prefer the absolute minimum so I don't have to worry too much when things change.
A way of finding out what's needed is to comment out one export at a time until your script breaks again. Then you know that variable is needed. Once it works with the maximum amount of export statements commented out, you can just delete those commented export statements altogether and what remains, however improbable, must be okay (with apologies to Sir Arthur Conan Doyle).
like func("rm -rf /usr/local/)
Which 'func' will you use to do that?
It depends on what you want to obtain.
If you want to display the ouput of the command that has been executed, passthru does the trick :
This function should be used in place
of exec() or system() when the output
from the Unix command is binary data
which needs to be passed directly back
to the browser. A common use for this
is to execute something like the
pbmplus utilities that can output an
image stream directly.
If you want to get the output of the command in a variable as a whole string, you can use shell_exec (which is the same as using the backtick operator) :
Execute command via shell and return
the complete output as a string
And, if you want to get it line by line, you'll use exec :
If the output argument is present,
then the specified array will be
filled with every line of output from
the command. Trailing whitespace, such
as \n, is not included in this array.
And, finally, if you need a bit more control, you'll use proc_open -- see the documentation, about that one ; it's a bit more tricky, I guess.
Still, when using those :
Don't forget you are calling a program that has to be available on the host machine
This means if you are using a Linux command, your application will most likely not work on windows
Depending on the configuration of PHP, it is possible that only a couple of commands could be available ; see safe_mode_exec_dir, about that
Using this means launching another processus -- probably not that good for performances
So, I'd use those only when it is really necessary.
You would use PHP's exec function. Other options include system, passthru, and the backtick operator.
Personally I think proc_open is the best approach.
http://php.net/proc_open
This gives you far more control over input, output and let's you check it's exit status and STDOUT, which is more than most other commands for system calls. It also doesn't hold up the system while the command exits.