I have something similar to this in a php script:
<?php
...
function log() {
// saving the log into a file.
exec(<<<BASH
cat >> $logFile <<EOF
$log
EOF
BASH
);
}
...
As you can see the two heredocs (BASH is php and EOF is shell) end as one would think is correct, but when I read the log created the log has something like this:
...
my logged string of an important event
EOF
my logged string of another important event
EOF
...
And I check the apache log and it has the following entries:
sh: line 1: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
What am I doing wrong?
Please, I am aware that there are many other implementations, such as using php functions or using quotes instead of heredocs. But I am curious about why in this particular case this does not work.
EDIT.
I clarified the code so it is more clear that I am talking about php running shell commands.
Updated answer for PHP case
Suppose we have test.php file with the following contents:
<?php
function mylog() {
$logFile = 'test.log';
$log = 'test';
exec(<<<BASH
cat >> $logFile <<EOF
$log
EOF
BASH
);
}
mylog();
Then php test.php produces the right thing(!):
rm -f test.log
php test.php
cat test.log
Output:
test
Now let's indent the Bash part:
<?php
function mylog() {
$logFile = 'test.log';
$log = 'test';
exec(<<<BASH
cat >> $logFile <<EOF
$log
EOF
BASH
);
}
mylog();
Now php test.php produces exactly what you've written about in your
question:
rm -f test.log
php test.php
cat test.log
Output:
sh: line 2: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')
test
EOF
Apparently, you have your Bash part indented, which is an invalid Bash syntax. So you just need to remove indentation for the Bash part. At least, EOF shouldn't be indented.
Original answer where I thought the OP meant pure Bash
exec executes a command, but you need to evaluate bash expression. So you need eval instead.
To construct the command using eval use the following:
eval "$(
cat <<'EOF'
cat >> test.log <<EOF2
log contents
EOF2
EOF
)"
So we constructed a Bash variable with "$( and )". Within the variable we created a here-doc string with cat <<'EOF' and EOF, where single quotes disable parameter substitution so we can enter literal text.(no evaluation). Then we've written log contents by means of another here-doc string created with <<EOF2 and EOF2.
We might save save the Bash variable, then use it as many times as we like:
cmd="$(
cat <<'EOF'
cat >> test.log <<EOF2
log contents
EOF2
EOF
)"
rm test.log
eval "$cmd"; eval "$cmd"; eval "$cmd"
cat test.log
Output:
log contents
log contents
log contents
See docs for here documents.
Related
This question already has answers here:
PHP standard input?
(10 answers)
Closed 1 year ago.
I was working on my project and suddenly stdin in PHP stops working.
I have code in PHP:
test.php
<?php
$stdin = file_get_contents('php://input');
echo ">>>>>";
echo $stdin;
echo "<<<<<";
and I'm executing it with:
echo 'HELLO' | php -f test.php
>>>>><<<<<
This works fine:
echo 'HELLO' | php -c "echo file_get_contents('php://input');"
HELLO
and it doesn't work, I'm spending way too much time on this. Why this simple code doesn't work? Any clues? I'm using Fedora GNU/Linux with php 7.4.19.
Your last example uses the -c parameter, from the man page:
-c
Look for php.ini file in the directory path or use the specified file
I guess you ment to use -r:
-r
Run PHP code without using script tags '<?..?>'
In your answer, the HELLO comes from the shell, if you replace -c with -r is shows nothing, for that, please continue reading
The above seen in shell, on my local machine, in a snippet because the answer is getting way to long
$
$ echo 'HELLO' | php -c "echo 'Test'; echo file_get_contents('php://input');"
HELLO
$
$ # HELLO Comes from shell, no 'echo 'Test';
$
$ echo 'HELLO' | php -r "echo 'Test'; echo file_get_contents('php://input');"
Test%
$
$ # With `-r` only the Test echo, because php:// input is empty
$
$
$ echo 'HELLO' | php -r "echo 'Test'; echo file_get_contents('php://stdin');"
TestHELLO
$
$ # If we use php://stdin we get both the echo's
$
From the documentation:
php://input is a read-only stream that allows you to read raw data from the request body
Your data is in stdin, so you'll need file_get_contents('php://stdin'):
php://stdin, php://stdout and php://stderr allow direct access to the corresponding input or output stream of the PHP process.
Source of above quotes
<?php
$stdin = file_get_contents('php://stdin');
echo ">>>>>";
echo $stdin;
echo "<<<<<";
Try it online!
Another option is to use stream_get_contents(STDIN):
<?php
$stdin = stream_get_contents(STDIN);
print(">>>>>");
print($stdin);
print("<<<<<");
Try it online!
I'm piping lines into a PHP script (see contrived example below). Alas the pipe unintentionally flows into the shell command in the script, thus nano doesn't run because it chokes on STDIN.
I want the shell command to run completely unrelated to the STDIN piped to the main script. So the PHP script should in a way "eat" the STDIN, so it doesn't reach the sub-shell. How do I fix this?
Note that exec(), system() and passthru() all give the same result.
$ echo -e "World\nEverybody" | php script.php
Hello World
Received SIGHUP or SIGTERM
Hello Everybody
Received SIGHUP or SIGTERM
script.php:
<?php
foreach(file("php://stdin") as $name) {
echo "Hello $name";
passthru("nano");
}
?>
Environment:
PHP 7.1.14 / PHP 5.6.30
GNU bash, version 3.2.57
GNU nano version 2.0.6
The pipe isn't really flowing into the sub-shells. In fact, nothing is flowing in. In order to connect nano's STDIN to the terminal, you pipe the controlling terminal (always /dev/tty) into nano, like this:
passthru("nano </dev/tty");
Here's an answer to your follow-up question. (Very good question IMO. My previous answer was slightly wrong in fact. STDIN does flow into the child processes.)
If the script consists of just passthru("nano") and you don't pipe anything into PHP, then nano works without </dev/tty. Why is this?
Linux behavior
In fact, child processes do inherit STDIN from their parent processes, but because of buffering, this isn't entirely clear sometimes. And since they inherit the same STDIN, when EOF is reached, they do whatever they do when EOF is reached (to see what nano does in this case, see below).
Let's take PHP out of the equation and see what we get when we turn buffering on or off. Here's some C code that will read from STDIN, system(), and read from STDIN again:
#include <stdio.h>
#include <stdlib.h>
int main() {
// setvbuf(stdin, NULL, _IONBF, 0 );
char buffer[32];
gets(buffer);
printf("Hello %s\n", buffer);
system("bash -c 'read FOO; echo This is bash, got $FOO'");
gets(buffer);
printf("Hello2 %s\n", buffer);
}
Compile (ignore the warnings about gets) and run:
$ cc -o script script.c
$ echo -e "Foo\nBar\nCar" | ./script
Hello Foo
This is bash, got
Hello2 Bar
bash didn't get anything. The gets after system magically got this input. Now uncomment the first line:
- // setvbuf(stdin, NULL, _IONBF, 0 );
+ setvbuf(stdin, NULL, _IONBF, 0 );
And we get:
$ cc -o script script.c
$ echo -e "Foo\nBar\nCar" | ./script
Hello Foo
This is bash, got Bar
Hello2 Car
This time bash got the second input. "Too long; didn't read": We do in fact have the same STDIN.
`nano` internals
First of all, you will find that nano's behavior is the same even if you take PHP out of the equation:
$ echo foo | nano
Received SIGHUP or SIGTERM
nano could in theory very well detect if we've got a terminal, and if we don't, attempt to open /dev/tty (it's just a regular open call). In fact, nano does this if you execute nano like this:
echo foo | nano -
The scoop_stdin function in src/nano.c takes care of this in version 2.9.4: http://git.savannah.gnu.org/cgit/nano.git/tree/src/nano.c?h=v2.9.4#n1122
And the finish_stdin_pager function in version 2.7.4: http://git.savannah.gnu.org/cgit/nano.git/tree/src/nano.c?h=v2.7.4#n1116)
So what happens when nano gets EOF? EOF in key input is handled like this:
Version 2.7.4: get_key_buffer() calls handle_hupterm(0) directly.
http://git.savannah.gnu.org/cgit/nano.git/tree/src/winio.c?h=v2.7.4#n207
Version 2.9.4: die(_("Too many errors from stdin"));
http://git.savannah.gnu.org/cgit/nano.git/tree/src/winio.c?h=v2.9.4#n207
(The reason I'm linking both is because the message changed at some point.)
I hope that sort of makes things clearer.
I am running a PHP script from the CLI. In that PHP script I would like to execute a few commands from the command line using system(). Perhaps this isn't the right tool for the job, and I could probably just wrap the PHP script in a shell script, but I want to know if it's possible to do completely within the PHP script.
When I do this in a terminal it works fine:
user#host:~$ workon pootle
(pootle)user#host:~$
However this doesn't work from PHP (being executed as the same user).
#!/usr/bin/env php
<?php
system('workon pootle'); // sh: 1: workon: not found
system('/bin/bash workon pootle'); // /bin/bash: workon: No such file or directory
system('/bin/sh workon pootle'); // /bin/sh: 0: Can't open workon
I notice that the output for the last two is exactly the same as when executing by a terminal, but the first one is different. I thought it might be an alias, however it doesn't appear to be. alias | grep -i workon shows no output.
I also compared all of the environment variables returned from command line env and PHP system('env'), and all but _ are exactly the same, including SHELL=/bin/bash.
The output of which workon executed by the terminal is empty.
Edit
Maybe I'm getting somewhere with this.
user#host:~$ type workon
workon is a function
workon ()
{
typeset env_name="$1";
if [ "$env_name" = "" ]; then
lsvirtualenv -b;
return 1;
fi;
virtualenvwrapper_verify_workon_home || return 1;
virtualenvwrapper_verify_workon_environment $env_name || return 1;
activate="$WORKON_HOME/$env_name/$VIRTUALENVWRAPPER_ENV_BIN_DIR/activate";
if [ ! -f "$activate" ]; then
echo "ERROR: Environment '$WORKON_HOME/$env_name' does not contain an activate script." 1>&2;
return 1;
fi;
type deactivate > /dev/null 2>&1;
if [ $? -eq 0 ]; then
deactivate;
unset -f deactivate > /dev/null 2>&1;
fi;
virtualenvwrapper_run_hook "pre_activate" "$env_name";
source "$activate";
virtualenvwrapper_original_deactivate=`typeset -f deactivate | sed 's/deactivate/virtualenv_deactivate/g'`;
eval "$virtualenvwrapper_original_deactivate";
unset -f deactivate > /dev/null 2>&1;
eval 'deactivate () {
# Call the local hook before the global so we can undo
# any settings made by the local postactivate first.
virtualenvwrapper_run_hook "pre_deactivate"
env_postdeactivate_hook="$VIRTUAL_ENV/$VIRTUALENVWRAPPER_ENV_BIN_DIR/postdeactivate"
old_env=$(basename "$VIRTUAL_ENV")
# Call the original function.
virtualenv_deactivate $1
virtualenvwrapper_run_hook "post_deactivate" "$old_env"
if [ ! "$1" = "nondestructive" ]
then
# Remove this function
unset -f virtualenv_deactivate >/dev/null 2>&1
unset -f deactivate >/dev/null 2>&1
fi
}';
virtualenvwrapper_run_hook "post_activate";
return 0
}
bash /usr/share/virtualenvwrapper/virtualenvwrapper.sh workon
I found this by installing virtualenvwrapper and then i ran
env | grep VIRTU
VIRTUALENVWRAPPER_PROJECT_FILENAME=.project
VIRTUALENVWRAPPER_SCRIPT=/usr/share/virtualenvwrapper/virtualenvwrapper.sh
VIRTUALENVWRAPPER_HOOK_DIR=/root/.virtualenvs
_VIRTUALENVWRAPPER_API= mkvirtualenv rmvirtualenv lsvirtualenv showvirtualenv workon add2virtualenv cdsitepackages cdvirtualenv lssitepackages toggleglobalsitepackages cpvirtualenv setvirtualenvproject mkproject cdproject mktmpenv
and saw VIRTUALENVWRAPPER_SCRIPT ... pretty easy to figure out from there
To be able to execute the virtualenvwrapper functions, we need to first source the virtualenvwrapper.sh file. To find the location of that file, in a terminal execute:
which virtualenvwrapper.sh
If that doesn't produce any output, use find to find it.
find /usr/ -name "virtualenvwrapper.sh"
For me, the location was /usr/local/bin/virtualenvwrapper.sh
When PHP does system calls, it uses sh:
<?php
system('echo $0'); // echoes 'sh'
However the virtualenvwrapper.sh script fails when being executed with sh, so we need to use bash instead.
Another thing to note is that PHP system calls are separate subshells from each other, so if you do:
<?php
system('FOO=BAR');
system('echo $FOO');
This prints out an empty line. Where as the following prints BAR:
<?php
system('FOO=BAR && echo $FOO');
Therefore, we need to:
Use bash instead of sh
Source the virtualenvwrapper.sh
Execute workon to change working virtual environments
Execute any other commands needed
Do all this in one single line
This is what I came up with and appears to be working correctly:
<?php
$commands = [
'source /usr/local/bin/virtualenvwrapper.sh',
'workon pootle',
'pootle update_stores --project=AIWeb',
];
system('/bin/bash -c "'.implode(' && ', $commands) .'"');
I'm trying to use PHP command line (from cron tab). I know how to add arguments like this:
cd /home/users/public_html/; php -f script.php some_value
I would like (or need) to add current date dinamically:
cd /home/users/public_html/; php -f script.php current_date
With wget I did this:
wget "https://mysitecom/script.php?currentdate=`date +\%s.\%N`"
But I can not find any way to do something similar wih a php command line.
I've tried:
cd /home/users/public_html/; php -f script.php `date+\%s.\%N`
And I get the error "Command not found".
I've tried also the solution proposed in one answer:
cd /home/users/public_html/; php -f script.php date+\%s.\%N
And I get the literal string "date+\%s.\%N"
With the other proposed solution:
cd /home/users/public_html/; php -f script.php "$(date +"%s.%N")"
I get these errors in the email sent by the cron:
/usr/local/cpanel/bin/jailshell: -c: line 0: unexpected EOF while looking for matching `"'
/usr/local/cpanel/bin/jailshell: -c: line 1: syntax error: unexpected end of file
When using PHP from the command line (CLI) they are not called GET variables they are called arguments
There are 2 parameters passed to every script run from the command line called $argv and $argc
One thing to remember the first argv[0] occurance holds the name of the script that is being run. Other than that the arguments appear in $argv[] in the order thay appear on the command line
argc is a count of how many variables have been passed to the script
arcv is an array of all the variables passed
If its any help they are just like the "C" equivalents if you have ever written any "C" code
ADDITIONAL INFO
To call your script with todays date use something like this
cd /home/users/public_html/; php -f script.php "$(date +"%s.%N")"
adjust the format as required.
Although if you want todays date in the script I am not sure why you would not get that from within the PHP script itself.
Finally I've made it work. It was a syntax error:
This:
cd /home/users/public_html/; php -f script.php `date+\%s.\%N`
Need to be:
cd /home/users/public_html/; php -f script.php `date +\%s.\%N`
Everyone knows PHP hates blank lines at the beginning or end of a file (before or after the PHP tags).
I've got an awk script that will modify the files. I pass all my files through it and things are peachy, no more leading or trailing blank lines.
I'd like to FIND the files first, to build a quick exception report.
I tried something like this:
grep -r -e :a -e '/^\n*$/{$d;N;};/\n$/ba'
But that's wrong.
This shell script will go through all your files and print if it found a blank line at the beginning or end of each file:
for f in `find . -type f`; do
for t in head tail; do
$t -1 $f |egrep '^[ ]*$' >/dev/null && echo "blank line at the $t of $f";
done;
done
I broke the lines for readability, but you can run it as a one liner too.
example output:
blank line at the head of ./b
blank line at the tail of ./c
blank line at the head of ./d
blank line at the tail of ./d