I've got to fix this little bug. First, let's talk about a small fact:
In CLI on Windows, you can't run a program with a space in its path, unless escaped:
C:\>a b/c.bat
'a' is not recognized as an internal or external command,
operable program or batch file.
C:\>"a b/c.bat"
C:\>
I'm using proc_open...proc_close in PHP to run a process (program), example:
function _pipeExec($cmd,$input=''){
$proc=proc_open($cmd,array(0=>array('pipe','r'),
1=>array('pipe','w'),2=>array('pipe','w')),$pipes);
fwrite($pipes[0],$input);
fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]); // max execusion time exceeded ssue
fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]);
fclose($pipes[2]);
$rtn=proc_close($proc);
return array(
'stdout'=>$stdout,
'stderr'=>$stderr,
'return'=>(int)$rtn
);
}
// example 1
_pipeExec('C:\\a b\\c.bat -switch');
// example 2
_pipeExec('"C:\\a b\\c.bat" -switch');
// example 3 (sounds stupid but I had to try)
_pipeExec('""C:\\a b\\c.bat"" -switch');
Example 1
RESULT: 1
STDERR: 'C:\a' is not recognized as an internal or external command,
operable program or batch file.
STDOUT:
Example 2
RESULT: 1
STDERR: 'C:\a' is not recognized as an internal or external command,
operable program or batch file.
STDOUT:
Example 3
RESULT: 1
STDERR: The filename, directory name, or volume label syntax is incorrect.
STDOUT:
So you see, either case (double quotes or not) the code fails.
Is it me or am I missing something?
Most unfortunately, the fix doesn't work as expected, however Pekka's first suggestion gave me an idea:
$file='C:\a b\c';
$cmdl='/d /b /g';
if(strtolower(substr(PHP_OS,0,3))=='win') // if windows...
$file='cd '.escapeshellarg(dirname($file)).' && '.basename($file);
_pipeExec($file.' '.$cmdl);
This is platform-specific, and I hope I don't have to fix this over linux as well. So far it works well!
Another way of solving this is by putting additional double quotes at the beginning and the end of the command.
$process = 'C:\\Program Files\\nodejs\\node.exe';
$arg1 = 'C:\\Path to File\\foo.js';
$cmd = sprintf('"%s" %s', $process, escapeshellarg($arg1));
if (strtolower(substr(PHP_OS, 0, 3)) === 'win') {
$cmd = '"'.$cmd.'"';
}
_pipeExec($cmd);
I have found this solution on https://bugs.php.net/bug.php?id=49139
It looks weird, but hey - it's Windows... :D
This is strage.
Untested workaround ideas:
Use a temporary environment variable:
exec('SET ENVPATH="C:\a b"');
proc_open('%ENVPATH%\c.bat' ....
(no idea whether this will work for proc_open)
Use the 8.3 filename if that can somehow be fetched in PHP - would certainly be doable using another exec()
proc_open() has an option to bypass cmd.exe - might be worth a try in case the filesystem somehow handles the quotes differently
Try escaping the quotes \"
Related
I hope you can help me.
I am dealing with a problem that I cannot solve. This is my issue. I am trying to exec a bash script through PHP. I tried with the method
exec()
with 3 arguments, arg1, arg2, and arg3.
php code
<?php exec("./randomScript.sh arg1 arg2 arg3"); ?>
randomScript.sh
.... # random code which exploits the three arguments -> executed normally..
.... # random code which exploits the three arguments -> executed normally..
./secondScript.sh $arg1 $arg2 $arg3 #<- this is the script that is not running (is not even started).
I have tried to change the permission (I've got full permission), change the way I call the randomScript.sh (through absolute path), but nothing occurred. Besides, I tried with:
shell_exec()
method, but nothing changed. Of course, if I run the secondScript.sh via terminal everything works fine.
I cannot understand which is the problem. Could you please help me?
Thanks in advance.
In the inner shell, the arguments are not called like that (because it is invoked by the shell, not by PHP).
./secondScript.sh "$1" "$2" "$3"
or just
./secondScript.sh $*
At this stage, just remember that spaces, quotes, and dollar signs in the arguments you pass must be avoided at all costs (the solution is escaping them, but how to do it exactly is tricky).
You also might want to do instead:
$ret = shell_exec("./randomScript.sh 'arg1' 'arg2' 'arg3' 2>&1");
so that in $ret you might find error messages and output from the inner shell.
You should escape your string command before passing it in exec function, i think it may help you
Escapeshellcmd()
...
$escaped_command = escapeshellcmd("./randomScript.sh arg1 arg2 arg3");
exec($escaped_command);
...
Escapeshellarg()
For system command, i recommand using system
...
$escaped_command = escapeshellarg("./randomScript.sh arg1 arg2 arg3");
system($escaped_command);
...
You also need to make sure that your PHP code does not change working dir, and both shell scripts have execute permissions, and filesystems allows to exec files on it.
I would avoid exec("./script $arg ...");, I would rather specify the interpreter to use and full path to the script like exec("sh /home/user/project/script.sh $arg ...");
I'm having some trouble using the PHP exec() function. Whenever the script I'm attempting to run is short, exec() works just fine. But if the script takes any more than a second or so, it fails. Note, I've attempted run the long script manually on the command line, and it works just fine. It seems as though the PHP interpreter is killing my external script if it takes any longer than a second or so to run. Any thoughts or suggestions? Here is my code:
<?php
$fileName = "foobar.docx";
$argVar = $fileName;
exec("python3 /var/www/html/jan8/alexandrina.py /var/www/html/jan8/$argVar");
echo "$output";
?>
And here is my script:
#!/usr/bin/env python3
import docx
import sys
docxFile = "".join(sys.argv[1:])
# The Three Lines Below Create a New Variable "htmlFile"
# "htmlFile" is the same as "docxFile", except ".docx" is cut off
# and replaced with ".html"
myNumber = len(docxFile) - 5
htmlFile = docxFile[0:myNumber]
htmlFile = htmlFile + '.html'
def generateHTML(filename):
doc = docx.Document(filename)
fullText = []
for para in doc.paragraphs:
fullText.append('<p>')
fullText.append(para.text)
fullText.append('</p>')
fullText.append('\n')
return '\n'.join(fullText)
file = open(htmlFile, "w")
file.write(generateHTML(docxFile))
file.close()
print("end python script")
Additional notes: I've increased the max execution time limits in php.ini, but I don't think that should matter, as the "long script" should only take a few seconds to run. Also, when I refer to the "short script", and the "long script", I'm actually referring to the same script. The difference between the "long script" and the "short script" is just the time to execute as it may vary depending on the size of the file I'm asking the script to process. Anyway... any suggestions would really be appreciated!
Ordinarily, php exec function should block until the command you run has completed. I.e., the PHP script will halt, waiting for the command to finish until continuing with the rest of your script. I was half thinking that your server was experiencing a max_execution_time timeout, but you've clearly stated that even just a couple of seconds is too long and even these fairly short scripts are having trouble.
A couple of solutions occur to me. The simplest one is to alter the python command so that a) any output is routed to a file or output stream and b) the process is run in the background. According to the docs on exec:
If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
I also would like you to make use of the two additional optional parameters for the exec function.
$fileName = "foobar.docx";
$argVar = $fileName;
$cmd = "python3 /var/www/html/jan8/alexandrina.py /var/www/html/jan8/$argVar";
// modify your command to toss output, background the process, and output the process id
$cmd_modified = $cmd . " >/dev/null & echo \$!";
$cmd_output = NULL; // this will be an array of output
$cmd_return_value = NULL; // this will be the return value of the script
exec($cmd_modified, $cmd_output, $cmd_return_value);
echo "exec has completed</br>";
echo "output:<br>" . print_r($cmd_output, TRUE) . "<br>";
echo "return value: " . print_r($cmd_return_value, TRUE);
This may help or may not. If it does not, we still might be able to solve the problem using posix commands.
EDIT: according to crispytx, the long scripts are resulting in a $cmd_return_val of 1 which means an error is happening. Try changing this one line:
$cmd_modified = $cmd . " >/dev/null & echo \$!";
to this
$cmd_modified = $cmd . " & echo \$!";
And let us know what the output of $cmd_output is -- it should at the very least have the process id of the newly spawned process.
Thanks for all the help S. Imp. I had a little trouble debugging using your suggestions because I happened to be using AJAX to call the script. However, I wrote simpler script using your suggestions to try and debug the problem and this is what I found:
Array ( [0] => Traceback (most recent call last): [1] => File "/var/www/html/jan8/alexandrina.py", line 28, in [2] => file.write(generateHTML(docxFile)) [3] => UnicodeEncodeError: 'ascii' codec can't encode character '\u2026' in position 25: ordinal not in range(128) )
So it looks like the problem has to do with ascii encoding! Even though the larger file was just a docx file with the same text as the shorter docx file repeated over and over again for 300 pages. It seems that if a docx file exceeds 1 pages, ascii characters are inserted that aren't present in single page docx files. I have no idea if this post will ever end up helping anyone, but who knows!
[SOLVED]
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 code something like this:
$file = fopen( "debug.txt", "w" );
$command = "myExe.exe param0 param1";
fprintf( $file, "starting\r\n" );
fflush( $file );
system( $command );
fprintf( $file, "the end...\r\n" );
fflush( $file );
It prints "starting" but not "the end...". The system() function hangs.
The myExe.exe is an applicatication written in C++, which actually terminates; i.e. the main function of myExe ends with a code like this:
FILE* f = fopen( "test.txt", "w" );
fclose(f);
return 0;
test.txt is created, which means "myExe.exe" works and finishes normally.
That problem does not occur each time we call the php file; sometimes hangs, sometimes works...
Any help&idea will be appriciated. Thanks in advance.
info;
OS: win xp
php server: wamp server 2.0
edit: my problem is not about my debug files. It is about system() or exec() functions. I can remove all other lines.
my php script works well for about 4/5 tries. After system() called, i call some sql functions but when system() hangs, my page will give a fatal error.
There is a known bug in php on windows (http://bugs.php.net/bug.php?id=44942).
There is a workaround you may want to try, close the session (call session_write_close() ) before calling the exec function.
Try using an sprintf to print the formatted text to a string, then a write, like so:
$file = fopen( "debug.txt", "w" );
$command = "myExe.exe param0 param1";
$startStr = sprintf( "starting\r\n" );
fwrite($file, $startStr);
fflush( $file );
system( $command );
$endStr = sprintf( "the end...\r\n" );
fwrite($file, $endStr);
fflush( $file );
Or, if you're not using any formatted strings (which in this case it doesn't look like you are), get rid of the sprintfs and just use write.
I would recommend you download/buy something like phpDesigner or RapidPHP so you can step through the logic of the program and see what exactly is going on. Nothing jumps out at me for being wrong with the program but if there is, either of the above programs will find it and display it to you in red.
You really do not need the "\r\n" unless you just like double spacing things. Just the "\n" should work fine.
Also, have you tried the "php -l " command yet to check for errors? It might turn up something.
Last, but not least, there are other commands in PHP to run programs externally - have you tried the other commands yet?
Just some thoughts. :-)
PS: I just had another thought: What are param0 and param1? If they contain special characters - that might influence what is happening with the system command.
PPS: AH! I may have a partial answer. The "return 0" line might be the culprit. Try changing it to "exit( 0 );" instead. The return statement is not the same as exit. It tries to return to the calling program and there is none. However, the system might get confused and think it should return to the PHP script (since all a return command does is a RET which causes the JSR from the system command to try to catch the RET. By replacing it with an exit command you are telling the system command you are through with your program. The system command will then do its own RET command back to PHP. If that makes sense. Basically, you are doing a double RET with the return command and you are also pushing the zero(0) status code onto the stack. Since the system returns its own status normally (via the exit command) what might be happening is that the null (0) is being interpreted as a stop command to the system command. IE: The zero gets popped onto the system stack, the RET is generated, the system pops off the RET leaving the zero(0 or null) byte which it isn't expecting and that freezes the system command. Again, the answer would be to switch to using "exit( 0 );" rather than the return command. Just a guess but I think that I ran into this years ago when I was doing Perl and the answer then was to use exit instead of return. So I'm figuring with PHP it might be the same problem. (Had to think about this for a while before I remembered it.)
I have a feeling, yes just a feeling, that there are open handles in your process preventing it from exiting. Most of the time when this happens it the STDIN and STDOUT handles. Did you check to see if fclose() in your C++ application succeeded?
Download:
http://technet.microsoft.com/en-us/sysinternals/bb896653
Use it to see how many handles are open for myEXE.exe when it hangs.
Did you happen to greatly simplify your myEXE.exe for the purposes of SO and missed something?
In php,I have to find whether a directory exist. IF it exists not a problem (I will display the hyperlink for that using the dirname)
Here is the sample in which I need help.
dir_name is the directory name
$url = system(~'ls -d /home/myapps/rel/".$dir_name"');
echo $url;(this does not work)
if(preg_match("/No such/",$url)) {
echo'Ther is no match'
}
else{
}
In my code the if block is never executed.(it should execute if the directory does not exist) ;(
Why don't you use is_dir() ?
http://php.net/manual/en/function.is-dir.php
As others have told, is_dir is the right way to go.
I'll point out why your existing program does not work. This will be useful in cases when you want to run an external command and then parse its output.
You have a unwanted ~ in your call
to system.
Variable interpolation does not
happen in single quotes. So you need
do something like:
system('ls -d /home/myapps/rel/'.$dir_name); or
system("ls -d /home/myapps/rel/$dir_name");
Even with the above changes it does
not work because if the dir does not
exist, ls issues the "....not
found" error on stderr and not on
stdout and system just returns the
last line of the stdout. To fix
this we need to redirect stderr of
the command to stdout as:
system("ls -d /home/myapps/rel/$dir_name 2>&1");
You also need to remember that
system returns only the last
line of the output. In the current
case if works, but in some other
cases if the external command being
run spits many lines of error/output
system will not work and you'll have
to use exec which allows you to
collect entire output lines in an
array. Something like:
exec("ls -d /home/myapps/rel/$dir_name 2>&1",$output_arr);
then you can search for your err string in the array $output_arr
So now you have the error message(if the dir does not exist) in $url and then check for the presence of string "No such".
Some more problems with the approach:
Some implementation of bash throw a
different err message when you list
for a non existing file. One I've
seen is /foo/bar not found and this
can cause your solution to break.
Also if the value of $dir_name
contains the string No such ( weird
but possible) your solution will
break.
http://php.net/manual/en/function.file-exists.php
bool file_exists ( string $filename )
Returns true if file or directory exists. If you then need to find it out if its directory or file use is_dir()