How to resolve unexpected token error with grep, regex, and PHP? - php

I know REGEX well enough but am stuck on this one. I am using PHP 5.4 to shell_exec() a grep statement with REGEX inside it to capture the SKU in a text file. In php I don't see an error but the grep doesn't work.
The goal is to capture the SKU in this text:
{'Product_Product_SKU': 'DOP36M94DPM'},
PHP Code:
$cmdSKU1 = 'grep -Po \'(?<=\{\'Product_Product_SKU\':\s{15}\').+?(?=\'\}\,)\' '.$FileName;
$chkSKU1 = trim(shell_exec($cmdSKU1));
Furthermore, when I look at the $cmdSKU1 variable in the debugger tool of the IDE I am using it's value is this as one line:
grep -Po '(?<=\{'Product_Product_SKU':\s{15}').+?(?='\}\,)' /opt/web/default/RetailerParsing/RetailerDownload/MDD30TMSTH__20220113091043.htm
Furthermore some more, when I paste it into the Ubuntu console it gives me the error:
-bash: syntax eror near unexpected token `)'
Even if I go back and escape the exposed single quotes. Infact, I escaped everything [^A-Za-z0-9] just to make sure! No beans.
Lastly, if I try to double escape the single quotes within the REGEX in the PHP IDE I get a red squiggly underline error under the words Produce_Product_SKU.

You're not escaping the shell command correctly. Instead of doing it manually, you should use ​escapeshellarg:
$cmdSKU1 = join(" ", array_map( 'escapeshellarg', array(
​"grep",
​"-Po",
​"(?<={'Product_Product_SKU':\s{15}').+?(?='},)",
​$FileName
)));
$chkSKU1 = trim(shell_exec($cmdSKU1));
That said, why not directly use preg_match and save a call to a non POSIX external command?
$string = "{'Product_Product_SKU': 'DOP36M94DPM'},";
preg_match("/{'Product_Product_SKU':\s{15}'([^']+)'}/", $string, $matches);
var_dump($matches[1]);
string(11) "DOP36M94DPM"
Of course you'll have to read the file inside php, but it may be worth the extra code.

You don't need regex at all here, and certainly not a shell_exec. Just split on ' and take the 4th (0-indexed) value out of the resulting array:
$str = "{'Product_Product_SKU': 'DOP36M94DPM'},";
$sku = explode("'", $str)[3];
var_dump($sku);
Yields:
string(11) "DOP36M94DPM"
Also note PHP 5.4 was end-of-lifed over 6 years ago. You really should update.

Related

preg_replace not working as expected replacing backslashes?

I was using the delimiters described in this answer:
Here is shell php I'm using to demonstrate the problem:
I'm using a function on a web project that replaces a namespace syntax for a path, eg:
\org\project\namespace\access
should be converted to:
/org/project/namespace/access
But I tried many ways, some threw the following warning:
Warning: preg_replace(): No ending delimiter '/' found in...
Which I don't want to show.
The warning above shows when using this as regex: /\\/
extra: (please look at the image) why it shows a bad encoded string?
UPDATE: why does it not work as this PHP Live Regex ?
You need to use "/\\\\/" instead of "/\\/" because \\ will produce \ (a single backslash) in a PHP string literal.
See http://php.net/manual/en/language.types.string.php
Why are you using regular expressions for such simple case? Stick to str_replace() or strtr():
echo strtr($str, ['\\' => '/']);

how to pass string containing newline to python in php

I have the following line of code in php that I m using to execute the python screen. and it takes in text from a html textarea which will contain newline characters
$string = $_POST['textarea']; // e.g. "String 1\n string 2\n"
$command = escapeshellcmd("python script.py -c \"$string\"");
when the script.py is executed, only "String 1" is received by the python script as arg. May I ask is there any native way for me to get the string passed?
If you feel that this is a duplicated question, please let me know where I can find this answer. I have been searching it all over stackoverflow.
I have managed to find a way to do this.
In the php file, replace all the newline with '\n' before calling escapeshellcmd
$string = trim(preg_replace('/[\r\n]+/', '\n', $_POST['textarea']));
// For a string like:
// abcd
// defg
// will become
// abde\\ndefg
Since the newline char '\n' has been escaped by php escapeshellcmd with the prepended slash. the shell will be able to process the string and python will receive abde\ndefg as one of the arguments.
Next in the python code, we simply have to undo the escape using the code below
arg = sys.argv[1].decode('string_escape')
I hope the above solution will be able to help those who are facing the similar issue as me. =)

How can execute php scripts in bash?

I need parse uri in shell scripts. So, I tried to use php in bash as below.
#!/bin/sh
uri="http://www.google.com?key=value
key="host"
value=$(php -r "$parse = parse_url('$uri'); echo $parse['$key']")
It has showing the following error.
PHP Parse error: syntax error, unexpected '=' in Command line code on line 1
Some body can help how to use embedded php in bash ?
A cheap way of debugging this is to use echo to see what you're passing in to php:
echo "$parse = parse_url('$uri'); echo $parse['$key']"
shows
= parse_url('http://www.google.com?key=value'); echo ['host']
You're already using $uri to mean "the value of the shell variable uri", so it's not surprising that $parse is also considered a shell variable and expanded to its value (unset, nothing).
Use \$ when you want a literal dollar sign in your double quoted string:
value=$(php -r "\$parse = parse_url('$uri'); echo \$parse['$key']")
You can use it easily, but you must be careful because of escaping in bash.
I recommend to use single quotes (you do not need to escape anything) and exit from the quotes when you want to do something special. Your example:
php -r '$parse=parse_url("'$url'"); echo $parse["'$part'"];'
Note that
you do not need to escapes $parse
you need to exit from single quotes when inserting bash variable: '$url'
you cannot use single quotes in the single quotes! do use double quotes " instead.
Update:
Just for the clarification - parse error happened because $parse was interpreted as bash variable (empty string) so the php command incorrectly started with =.

PHP exec() with double quote argument

I've been having trouble running a command using PHP's exec() function on Windows. Per a comment on PHP's site on exec():
In Windows, exec() issues an internal call to "cmd /c your_command".
My command looks like:
"path\to\program.exe" -flag1 attribute1 -flag2 attribute2 -flag3 "attribute3 attribute4"
Under regular execution of this command in my local command prompt, without the /c flag, the command runs fine. However, with the introduction of the /c flag, command prompt tells me that "The system cannot find the path specified."
I think the command prompt is interpreting the double-quoted argument as a path to another file, but that's the furthest I've gotten with this problem.
Does anybody have any ideas on how to get past this? Thanks!
I also encountered this issue, and the cause of it is, indeed, the internal use of "cmd /c" as described in your own answer.
I have done some investigation, and have found that this was resolved in PHP 5.3, which is why some commenters were unable to reproduce it.
It was fixed in the following commit:
https://github.com/php/php-src/commit/19322fc782af429938da8d3b421c0807cf1b848a#diff-15f2d3ef68f383a87cce668765721041R221
For anyone who still needs to support PHP 5.2, it is fairly easy to replicate the fix in your own code. Instead of:
$cmd = "...any shell command, maybe with multiple quotes...";
exec($cmd);
use
function safe_exec($cmd, &$output = null, &$result_code = null) {
if (strtoupper(substr(php_uname('s'), 0, 3)) == "WIN"
&& version_compare(PHP_VERSION, "5.3", "<"))
{
$cmd = '"' . $cmd . '"';
}
return exec($cmd, $output, $result_code);
}
$cmd = "...any shell command, maybe with multiple quotes...";
safe_exec($cmd);
This replicates the behaviour of PHP 5.3 and above, in the same way as in the above-linked commit.
Note that this applies to all shell commands, not just exec() as used in my example.
I've figured out the answer by myself...
After perusing cmd.exe's /? and trying to decipher that, I've noticed one important section:
If all of the following conditions are met, then quote characters on the command line are preserved:
No /S switch (Strip quotes)
Exactly two quote characters
No special characters between the two quote characters, where special is one of: & < >( ) # ^ |
There are one or more whitespace characters between the the two quote characters
The string between the two quote characters is the name of an executable file.
Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character. To negate this behaviour use a double set of quotes "" at the start and end of the command line.
It seems as though if there more than one pair of quotes at any time, quotation marks will be stripped from the second pair of quotes and on.
A relevant question: How do I deal with quote characters when using cmd.exe but not completely relevant, since PHP will not allow you to modify its exec() command by putting an /S flag on its call (which would definitely be a lot easier).
I've managed to work around this problem by directly changing directories with chdir() to the folder where the .exe is located, then chdir()'ing back to my original working directory. It's an extremely hacky solution, given the case that I'm lucky that I only have one set of arguments using double quotes. If anybody were to have multiple arguments using double quotes, I wouldn't know how to solve that...
Just a guess (I am not familiar with PHP on windows): maybe escape the quotes as " becoming ""?
"path\to\program.exe" -flag1 attribute1 -flag2 attribute2 -flag3 ""attribute3 attribute4""
Whatever the solution is, make sure that when there's some form of user-input that gets passed to this command as arguments that you use escapeshellarg and/or escapeshellcmd.
I hope it will help
escapeshellarg() — Escape a string to be used as a shell argument
escapeshellcmd() — Escape shell metacharacters

PHP exec() and spaces in paths

I'm executing the following in a PHP application:
$source = '/home/user/file.ext';
$output_dir = $this->setOutputString();
chdir('/home/ben/xc/phplib/bgwatcher-2011a/a01/');
exec('php bin/createjob.php $source $output_dir', $output);
return $output[0];
The problem is this: I have control over $source, but not $output_dir, which is a legacy Windows filesystem, and there are spaces in the path. An example $output_dir is:
/home/vol1/district id/store id/this_is_the_file.html
When inserting the output string into the exec() function, I have tried both:
addslashes($output_dir) and '"' . $output_dir . '"' to escape the entire output string. In the first case, the path gets concatenated to:
/home/vol1/districtthis_is_the_file.html
... where everything between the first space and the filename gets dropped. In the second case, exec() appears to throw a shoe and doesn't execute properly - unfortunately, the error message is getting lost in the machinery - I can provide it if it's absolutely necessary, but I'm also under time constraints to find a solution.
What's the solution, here? Do I sprintf() the entire string for exec()? I'm very confused as to why addslashes isn't working correctly to escape the spaces, and I assume it has something to do with sanitization with exec(), but I can't find any documentation to back it up.
Update: I've tried escapeshellarg() and preg_replace() without success. Thinking about this further, do I need to double-escape the path? Or escape the path and the command? If the path is being unescaped once by exec(), and once by PHP before it executes the command, does it stand to reason that I need to account for both escapes? Or is that not how it works?
I don't believe addslashes() does anything with spaces. escapeshellarg() might be what you want instead. Docs on escapeshellarg
From the PHP doc (here),
Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).
This won't do anything to the spaces. What you will need to do is use str_replace() to add slashes, like this:
$new_string = str_replace(" ", "\\ ", $old_string);
According to the PHP docs,
Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).
Looks like you'll have to preg_replace the spaces yourself.
Edit:
Even though this is the topic of another discussion, if performance is an issue, then after looking into it a little more, it seems that str_replace is actually quite a bit faster than preg_replace:
The test labeled "str_replace()" was
the faster by 0.9053 seconds (it took
10.3% the time.)
The first test took 1.0093 seconds. (preg_replace)
The second test took 0.104 seconds. (str_replace)
Benchmark found here.
I've used exec() with paths with spaces before, on both Windows and Linux hosts, and in both cases quoting the path worked perfectly for me.
That said, if you have no control over the safety of a shell argument, always run it through escapeshellarg() first!
You can very well use shell quotes, since that is what all exec commands run through:
exec("php bin/createjob.php '$source' '$output_dir'", $output);
It btw works not just for arguments, but also for the command itself:
exec('"/bin/echo" "one parameter"');
Use escapeshellcmd() anyway.
this works for me when using exec() with soffice(LibreOffice):
$file_name = "Some, file name.xlsx";
exec('/usr/bin/soffice --headless --convert-to pdf '."'".$file_name."'".' 2>&1', $output, $r);
You can use double quotes and escape character together to work out this.
$fileName = "filename with spaces.pdf";
exec("php bin/createjob.php >\"".$fileName."\" 2> error.log" , $output, $return);

Categories