Does PHP shell_exec add parentheses to the command? - php

I'm trying to run a cat command using the shell_exec function, to be more precise something like this:
cat <(echo "foo") bar.xml > foo-bar.xml
But I'm getting a syntax error like the following one:
sh: 1: Syntax error: "(" unexpected
I'm completely lost since this works fine locally and when the command is executed manually in the server, but when running the php script it returns the syntax error. Any clues?
Original code being used:
$shell_cmd = "cat <(echo \"{$this->xmlHeader}\") ";
$shell_cmd .= '\'' . $path . $filename . '\'' . " ";
$shell_cmd .= " > " . '\'' . $path . "hfb/" . strtolower(str_replace($this->replace_values, '', $hfbName)) . ".xml" . '\'';
shell_exec($shell_cmd);

The problem here is likely to be which shell is used. It's not really documented, but I believe shell_exec will use /bin/sh, which will often be a minimal Posix-compliant shell (or a more complex shell emulating that compliance). That's very useful, because it means system scripts will always work the same way, regardless of what other shells are installed or configured for particular users.
When you log in directly, however, you're probably using bash, which has extra features such as the <(...) syntax for getting a named file descriptor from a command.
The best approach in this case is to make your command use only standardised facilities, which will be available in /bin/sh.
In particular, you are using cat to glue together a literal string from echo and a file:
cat <(echo "foo") bar.xml
That can be expressed instead by first echoing the string, and then outputting the file:
echo "foo"; cat bar.xml
To gather both into one output, place them in braces:
{ echo "foo"; cat bar.xml; } > foo-bar.xml
Alternatively, you can give cat an argument of - to concatenate standard input with one or more other files, so you could pipe the value from echo into it:
echo "foo" | cat - bar.xml > foo-bar.xml

Related

How can i implement escapeshellarg inside a URL?

The user supplies two variables via a HTML form called username and name.
They are used to execute a shell command which is currently unsafe.
I have the following PHP code:
(exec("cat /opt/application/userdata/$username/following | grep -w $name"))
I am trying to implement escapeshellarg, but can't get it working by following the official PHP documentation, i have tried:
(exec("cat /opt/application/userdata/ .escapeshellarg($username)/following | grep -w .escapeshellarg($name)"))
But this is not working and i think its a syntax error.
How do i format this properly?
What's happening is that you are currently trying to run a function inside of a string. This is possible with extra steps, but is not desirable.
What you want to do is concatenate the string with the output of the function.
You can inline that in this manner:
exec('cat /opt/application/userdata/' . escapeshellarg($username) . '/following | grep -w ' . escapeshellarg($name))
(noticed I used single quotes ['], as no expansion is happening within the string, this is somewhat faster and keeps it separate)
Or you can perform the operation earlier, and simply include ("expand") the variables in the string, like your first example hints at:
$username = escapeshellarg($username);
$name = escapeshellarg($name);
exec("cat /opt/application/userdata/$username/following | grep -w $name")
(noticed I used double quotes ["] as there is expansion happening within the string)
The escapeshellarg() function is a PHP built-in, but you're attempting to execute it as a shell function. You can fix it by simply pulling the function out of the string scope and concatenating the results:
exec("cat /opt/application/userdata/" . escapeshellarg($username) . "/following | grep -w " . escapeshellarg($name));

How to create alias for bash inside php exec?

I have code like this:
public function shell($code) {
$code = preg_replace('/"/', '\\"', $code);
exec('bash -c "' . $code . '"', $result);
return $result;
}
and I want to add alias ls="ls --color=always". I've try to put it in .bashrc file that I've created just for that shell in my project directory and use:
exec('bash -c ". .bashrc;' . $code . '"', $result);
but this don't work, I'm in correct directory because I see that file when I call ls -A.
I've also try --init-file and --rcfile with just a file and full path.
How can I add aliases and functions to that shell? Is it possible?
Using functions is probably a better choice anyway. However, note that it is possible to use aliases if you set the expand_aliases option:
<?php
$code = 'ls';
$aliases = '
shopt -s expand_aliases
alias ls="ls -l"';
$code = $aliases . "\n" . $code;
exec('bash -c ' . escapeshellarg($code), $result);
echo implode("\n", $result) . "\n";
Output:
$ php aliasexec.php
total 12
-rw-rw-rw- 1 mlk mlk 198 Feb 18 11:18 aliasexec.php
This is what the man page has to say (emphasis mine):
Aliases are not expanded when the shell is not interactive, unless
the expand_aliases shell option is set using shopt […].
The rules concerning the definition and use of aliases are somewhat
confusing. Bash always reads at least one complete line of input
before executing any of the commands on that line. Aliases are
expanded when a command is read, not when it is executed. Therefore,
an alias definition appearing on the same line as another command
does not take effect until the next line of input is read. […] To
be safe, always put alias definitions on a separate line, and do not
use alias in compound commands.
For almost every purpose, aliases are superseded by shell functions.
That is why you must use newlines and not the ; character to define the alias.
The only thing that work is to add functions instead of aliases:
function ls() {
/bin/ls --color=always $#;
}
function grep() {
/bin/grep --color=always $#;
}

Intercepting standard output / Passing application output (Shell/C/PHP)

I am running a hybrid of shell script and a C application to allow execution as root. I am using it to do some conversions with FontForge from the website that is written in PHP. My problem is that when FontForge encounters some problems its spewing error information out to standard output. I am currently capturing that output and parsing it for keywords to generate some error messages.
Q: I am wondering if I can somehow redirect that output to some variable and pass it back to PHP for processing - as long as current solution works fine under browser, unfortunately when I run unit tests I get pages of failed glyph mapping information that does nothing but obscures the results. I would like to bypass std_out entirely.
I am not very very familiar with either C or shell scripting so please do not laugh :). Here is what I have:
PHP:
[...]
$new_path = exec("./convert_font " . $file . " " . $file2);
if (strpos($new_path, 'Save Failed') !== false) {
// throw exception or something
}
[...]
Script (convert_font):
#!/bin/bash
PATH=/usr/local/bin:$PATH
FONTFORGE_LANGUAGE=ff
export PATH FONTFORGE_LANGUAGE
if (test -f $1);
then
a=$(./pfb2otf $1 $2 2>&1)
fi
echo $a
C (pfb2otf):
#!/usr/bin/fontforge
//Opens file
Open($1);
Reencode("unicode");
//Makes conversion to otf
Generate($2+".otf");
//Prints the resulting name (if conversion is successful) to STD_OUT so I can capture it with my bash script to send back to PHP and consider operation successful to
Print($2+".otf");
Quit(0);
http://php.net/manual/en/function.exec.php
output
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. Note that if the array already contains some elements, exec() will append to the end of the array. If you do not want the function to append elements, call unset() on the array before passing it to exec().
You can use the additional output parameter in your PHP Code to catch all stdout messages to an array. You can also redirect stderr to stdout in both perl and C
http://www.masaokitamura.com/2009/08/how-to-redirect-stderr-to-stdout-in-perl/
Hope this helps.
There are 3 things to note :
1) Your program writes probably errors on error output, to get them too you need to add 2>&1
$new_path = exec("./convert_font " . $file . " " . $file2 . ' 2>&1');
2) exec() returns only the last line of the command execution, the safest way to get your return values is to pass a second argument to your exec().
$return = array();
exec("./convert_font " . $file . " " . $file2 . ' 2>&1', $return);
You may note that $return is a multidimentional array with 1 line / entry. So to be sure you get your error into this array, you may do :
$new_path = implode("", $return);
3) do not forget to use escapeshellarg in case where one of your file has spaces (at least) or back quotes/parenthesis/dollar (...everything a shell can interpret).
$return = array();
exec("./convert_font " . escapeshellarg($file) . " " . escapeshellarg($file2) . ' 2>&1', $return);

PHP: Backtick failing to work with command

I have a PHP script that retrieves 200 lines from a file by executing a command in Bash using backtick operators. Here's what the code looks like:
$endline = `(shell execution that returns a number here)`;
$line = $endline - "200";
$lines = "sed -n '".$line.", ".$endline." p' log.txt";
echo $lines;
$file = `$lines`;
echo $file;
This code returns $lines as sed -n '1800, 2000 p' log.txt, but $file doesn't return any results. When directly using sed -n '1800, 2000 p' log.txt in a Bash terminal, I get the expected results.
What is done incorrectly here? Do the ' characters have to be escaped?
Edit: The shell script added a space after the number, therefore misreading it.
My guess is that it's $eof or that your path (log.txt) is not appropriate.
I copied and pasted your code, and it works with the following tweaks:
syntax error fixed (add ; to echo $lines)
change $eof to $endline (though you may not need to if $eof is valid
ensure that log.txt was a valid path (this is most likely your error)
otherwise, it ran as expected.
The reason it would work in Bash but not in PHP is that their "working directory" is not necessarily the same.

Bash script to alter serialized PHP data

I have many files containing php serialized data in which I have to replace some strings by another one. The linux host doesn't have any php installed. The problem is to adjust the modified string to correct size.
I tried something like to replace /share path to /opt:
sed -re 's~s:([0-9]+):"/share([^"]*)~s:int(\1-2):/opt\2~g' file
but the result file is bad: lengths are litteral expression int(size - 2)
Any idea ?
This solution isn't ideal, but you could use perl:
my $line;
while ($line = <STDIN>) {
$line =~ s~s:([0-9]+):"/share([^"]*)~"s:".($1-2).":\"/opt$2"~ge;
print $line;
}
Hopefully I've understood your requirements correctly. Here's an example:
php -r 'echo serialize(array("/share/foo")) . "\n";'
a:1:{i:0;s:10:"/share/foo";}
php -r 'echo serialize(array("/share/foo")) . "\n";' | perl replace.pl
a:1:{i:0;s:8:"/opt/foo";}
EDIT: Here's a modified script to edit the file in-place with variable search and replace strings.

Categories