I'm running on win2003 server, PHP 526, via the cmd-line.
I have a cmdline string:
$cmd = ' "d:\Prog Files\foo.exe" -p "d:\data path\datadir" ';
Trying to do this in php code
$out = `$cmd`; # note use of backticks AKA shell_exec
results in a failure by foo.exe as it interprets the -p arg as "d:\data".
However, the same $cdm string copied to the windows shell cmdline executes successfully.
How do I properly handle spaces in PHP shell_exec?
Use escapeshellarg() to escape your arguments, it should escape it with an appropriate combination of quotation marks and escaped spaces for your platform (I'm guessing you're on Windows).
Unlike Unix, which requires quotes within quotes to be escaped, Windows doesn't seem to work this way. How it keeps track of the quotes is beyond me, but this actually seems to work, despite all logic to the contrary:
$cmd = '" "C:\Path\To\Command" "Arg1" "Arg2" "';
$fp = popen($cmd, 'r');
$output='';
while ($l = fgets($fp, 1024))
$output.=$l;
I'm guessing command.exe is taking the command string total and nixing the (otherwise redundant) outside quotes. Without these outside quotes, DOS does some weird things. This solution is similar to post by user187383, but does away with the "cmd /c" which only obfuscates what's actually happening along with a mild performance cut, since cmd /c is implicit by the shell call in the first place!
This is an interesting problem. Apparently, PHP lets you put double quotes around the program or the arguments, but not both. It may be worth reporting this as a bug.
A work around is to use the DOS 8.3 name instead of quotes. E.g., "C:\Program Files\" usually becomes "C:\Progra~1".
Had this problem too - came up with an idea to route the launching through cmd.exe. The trick here is not to get lost in the double qoutes. Generally you want to put anything you want to run in:
exec('cmd /c " '.$path.' "';
Where $path is a already double-quoted path to your executable. Example:
$path = '"C:\Program Files\ToDoList Simple\ToDoList.exe" "C:\SomePath\todo.tdl" -nt test -cm test2';
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
I have come across an interesting situation with the shell_exec() function in PHP. This command works:
$res = shell_exec("mdb-export -D \"%d/%m/%y\" ".$this->mdbFileName." ".$tableName." &> " . $outputFile);
However, this command does not work:
$res = shell_exec("mdb-export -D \"%d/%m/%y\" ".$this->mdbFileName." \"".$tableName."\" &> " . $outputFile);
When entering the command in a shell (either way - with or without quotes around the table name) it works fine. I would really like to know why the first set of escaped quotes does not affect shell_exec():
\"%d/%m/%y\"
But the second set:
\"".$tableName."\"
Does not allow shell_exec to execute. FYI: all file name variables in the script are full file paths. They were entered in the exact same way when testing in the shell. Any thoughts?
EDIT: Upon failure, shell_exec returns NULL and the command is not executed.
After some more testing, it appears that the $tableName variable contained an extra space. Another part of the program was pulling the table name from the MDB file and left a trailing space.
When echoing out the $tableName variable everything appeared fine because the space was invisible. When running shell_exec without quotes surrounding the table name, the command executed because no errors occur if there are extra spaces between parameters. However, when surrounding the variable with quotes, the mdb-export program started looking for:
"tableName "
instead of
"tableName"
causing a silent, yet complete failure.
how can i run an ".exe" file throw my PHP class?
Or is there any other way to do that?
I have already used
exec();
passthru();
function but its give me error, my code :
$path = "C:\Program Files\Softland\doPDF 7\dopdf.exe";
$result = passthru('$path 2>&1');
Error :
'$path' is not recognized as an internal or external command, operable program or batch file.
This isn't a good practice but ....
$result = passthru($path.' 2>&1');
or
$result = passthru("$path 2>&1");
you should (as you can see) put $path variable outside single quotes (that prevent variable evaluation and substitution) or put double qoutes (that permits variable substitution)
You need to use double quote if you want $path to be replaced by variable value so
$result = passthru("$path 2>&1");
The problem is that you're using single quotes, so the string passed to the shell was literally '$path 2>&1'. Variable substitution does not occur in side single quotes, only double, in PHP.
So you need to either concatenate the string or put it in double quotes.
$result = passthru("$path 2>&1");
I'm writing a php CLI script that accepts, among other argument, a path.
So an example is:
php myscript.php -p=/Volumes/Macintosh HD/Users/andrea/samples
The script has his own way to red the arguments and it properly gets the value for -p, setting it in a variable called $project_path.
However, when I test the folder with isdir($project_path) it returns false.
I've tried to pass the path in different ways:
/Volumes/Macintosh\ HD/Users/andrea/samples
'/Volumes/Macintosh HD/Users/andrea/samples'
"/Volumes/Macintosh HD/Users/andrea/samples"
'/Volumes/Macintosh\ HD/Users/andrea/samples'
"/Volumes/Macintosh\ HD/Users/andrea/samples"
Non of them works.
What's the format I must use to make it work?
Please consider that the script must also work on different OS (i.e. Windows).
The problem is the path argument is automatically escaped:
I need to unescape it.
The returned string is:
\'/Volumes/Macintosh\ HD/Users/andrea/samples\'
Short answer: Use escapeshellarg()
Long answer:
chmod +x yourscript.php
-
$path = '/Volumes/Macintosh HD/Users/andrea/samples';
$cmdline = sprintf('/home/user/yourscript.php -p=%s 2>&1', escapeshellarg($path));
$output = shell_exec($cmdline);
Example cli script:
#!/usr/bin/php
<?php
fwrite(STDOUT, print_r($_SERVER, TRUE));
exit(0); // exit with exit code 0
?>
I eventually used the getopt() to get unescaped arguments (I don't know why there is this difference) and str_replace( array( "'", '"'), '', $file_path ); to remove the wrapping quotes.
I have a Ruby script that generates a sed command to replace some PHP code. The command is later executed via SSH.
command = "sed -i \"s*#{find_what}*#{replace_with}*\" #{file} "
The replace_with string will contain multiple lines of PHP code so the following escaping needs to take place:
command.gsub!(/\n/, '\\\n ') # Handle new-line escaping
command.gsub!(/&/, '\\\&') # Handle RegEx variable escaping
command.gsub!(/(\$[a-zA-Z0-9_]+)/) { |s| s.gsub!(/\$/, '\\$') } # Handle bash variable escaping
The command after escaping looks like this:
sed -i "s*require_once('file.php');*\n require_once(\$www_dir . \$path . '/file.php');\n *" /var/www/something.php
When executing this command manually everything works as expected. However if I execute the command via Kernel.system all the PHP variables are stripped out in the replacement string. Something.php ends up looking like this:
require_once( . . '/file.php');
Any ideas would be appreciated.
Thanks.
Update: try having single quotes around the sed command that way no bash variable substitution will be run. I'd try the ruby stuff out like this until it looks just right.
puts `echo #{command}`
If you're using SSH anyway, I'd just cook something up like this to be able to run it locally via ssh, makes it super easy to have total control by keeping it all ruby.
require 'net/sftp'
Net::SFTP.start('host', 'username', :password => 'password') do |sftp|
filedata = sftp.file.open("/path/to/remote", "r").read
filedata.gsub!(/foo/, "bar")
sftp.file.open("/path/to/remote", "w") do |f|
f.puts filedata
end
end
see http://net-ssh.rubyforge.org/sftp/v2/api/