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) .'"');
Related
I have a php web application and on one of the route I try to execute node script.
Node script location: C:\Users\meusr\Workspace\www\myproject\directImport.js
I have configured my package.json, such that npm start executes above script.
php (laravel) application's executable index.php located at: C:\Users\meusr\Workspace\www\myPHPproject\public\
And on one of my route: importer/run
I have:
$commandToRun = "npm start --prefix ". env('IMPORTER_PATH'). " /dev/null 2>&1";
Where IMPORTER_PATH is configured in my env, as: IMPORTER_PATH = C:\Users\meusr\Workspace\www\myproject\
when I try to execute the command with exec
exec($commandToRun, $output);
Page keeps on loading no output, no error and no end.
The script has no issue and running the same command on command line on windows system works:
npm start --prefix C:\Users\meusr\Workspace\www\myproject\
outputs as expected.
I thought it was issue with permission first (which should have thrown error, but I still tried and ran another command)
exec('npm -v', $output);
which outputs my npm version.
Similarly I tried to use simply node directImport.js which didn't work either.
Then tried to change directory to the location where node file is located and ran the command again.
exec(cd C:\Users\meusr\Workspace\www\myproject\ && dir /dev/null 2>&1) // this works, but:
exec(cd C:\Users\meusr\Workspace\www\myproject\ && node directImport.js /dev/null 2>&1) // this didn't work
Thanks to miken32, what worked for me was:
<?php
$commandString = 'start /b c:\\programToRun.exe -attachment "c:\\temp\file1.txt"';
pclose(popen($commandString, 'r'));
?>
http://php.net/manual/en/function.popen.php#92413
I'm using exec to execute some unix commands, like this one :
exec('cd /var/www/html/ && export PATH=$PATH:usr/loval/bin/npm && npm run dev; 2>&1', $output, $return_code_npm_run_dev);
I need to have the output with the error, so I put 2>&1 in the end of the command. But, when I do this, the return code (here : $return_code_npm_run_dev) is always 0. When I remove 2>&1, the return code is what I want (1 if something is wrong, 0 if all is ok.)
My questions are : why exec send me 0 when I redirect stderr ?
Is there another way to have the good return code and the stderr printing ?
Get rid of the semi-colon here:
npm run dev; 2>&1
I'm trying to create a PHP wrapper class around the Linux Screen utility.
I need to be able to start a screen session by name and get back the PID of the screen that was created.
I cannot assume the session name is unique so it can't parse the session -ls output to find the PID.
Based on examples and suggestions around the internet, I have tried the following approach using PHP's exec() function:
screen -D -m -S 'screen_name' 2>&1 &
The -D parameter tells screen to not fork the process, so it has the same PID as the background process.
So, then I can parse the PID from the output of the & background shell operator which is in this format:
[<number>] <pid>
This command works from the Linux shell (terminates immediately), but when I run it from PHP it freezes and the browser never loads the page.
I have tried executing it with both PHP's exec() and shell_exec() and it's the same result.
I have also tried lots of different combinations with the last part of the command such as the ones described HERE.
Edit:
If I use this command:
screen -D -m -S 'screen_name' 1> /dev/null 2> /dev/null & 2>&1
Then the PHP exec() works. It starts the screen session but then I don't get any output from the & background operator. I'm assuming it's because I'm redirecting the stdout and stderr to /dev/null but as far as I know it should only be doing that for the screen command, not the & operator.
ALMOST SOLUTION:
screen -D -m -S 'screen_name' 1> /dev/null 2> /dev/null & echo $!
I realized that the text showing in the terminal from the & background operator wasn't actually coming from the command stdout or stderr. It's the shell displaying it. So I added echo $! after the & operator which will print the PID of the process created by the last command.
This works in the terminal, it prints the correct PID. But when it's executed in PHP, the actual screen PID value is 4 more than the one returned by the echo.
It's like the echo is being executed before the & operator. Is there any way to make it wait?
SOLUTION:
I was wrapping the command in sudo, and the background operator was acting on the sudo command instead of the screen command. Make sure you escape your command arguments! :)
ALMOST SOLUTION:
screen -D -m -S 'screen_name' 1> /dev/null 2> /dev/null & echo $!
I realized that the text showing in the terminal from the & background operator wasn't actually coming from the command stdout or stderr. It's the shell displaying it. So I added echo $! after the & operator which will print the PID of the process created by the last command.
This works in the terminal, it prints the correct PID. But when it's executed in PHP, the actual screen PID value is 4 more than the one returned by the echo.
It's like the echo is being executed before the & operator. Is there any way to make it wait?
SOLUTION:
I was wrapping the command in sudo, and the background operator was acting on the sudo command instead of the screen command. Make sure you escape your command arguments! :)
– Bradley Odell
I am trying to setup a web-based portal through which we can checkout different branches of our Git repository through a simple click on a back-end panel.
So currently, I have /var/www/devportal which contains index.php, status.sh and checkout.sh
In index.php I do the following:
$repo = $_GET['repo'];
$command = 'sh status.sh ' . $repo;
$output = exec($command);
echo "<pre>$output</pre>";
The contents of status.sh are:
#!/bin/bash -e
if [ $# -ne 1 ]
then
echo "Usage: `basename $0` <repo name>"
exit 1
fi
cd /var/www/$1
git status
And this works just fine. The output echoed in PHP shows me the status of the current branch within /var/www/proj.
Now when I try to do the same thing (passing 2 parms this time with the second one being the name of the branch to checkout) with checkout.sh, who'se contents are:
#!/bin/bash -e
if [ $# -ne 2 ]
then
echo "Usage: `basename $0` <repo name> <branch name>"
exit 1
fi
cd /var/www/$1
git checkout $2
It doesn't work. Not only does it not work, I don't get diddly squat for an error message. There is no output. I know that the checkout.sh script works fine because when I echo the command that is being sent via PHP's exec command, copy that exact thing and run it via terminal logged in as root, it works just fine, does the checkout and returns the name of the newly activated branch.
Any tips on this would be greatly appreciated. My box is pretty standard, Ubuntu 10.04 and running Apache2.
Thanks!
exec fills $output with your command standart output, to show error (if any) add "2>&1" at the end of your command.
exec can also tell you the return value, try:
$output = exec($command, $array_output, $ret_val);
var_dump($ret_val);
echo "<pre>$output</pre>";
$repo = $_GET['repo'];
$command = 'sh status.sh ' . $repo;
$output = exec($command);
Oh jesus man. Don't do this. escapeshellarg exists for a reason
I have this in post-commit:
#!/bin/sh
REPOS="$1"
REV="$2"
/usr/bin/php /home/name/svn/scripts/post-commit.php $REPOS $REV
But whatever I do, post-commit.php isn't being executed, not even with a chmod a+rw on it. Also there is no output from exec.
What am I missing?
Update: removed exec > ./logs/log.txt from this example since it seems to confuse people.
try:
#!/bin/sh
REPOS="$1"
REV="$2"
#debug:
echo "------------------------------"
date >> /tmp/debug.txt
echo "$#" >> /tmp/debug.txt
id >> /tmp/debug.txt
env >> /tmp/debug.txt
/usr/bin/php /home/name/svn/scripts/post-commit.php "$REPOS" "$REV" > /full/path/to/log.txt 2>&1
Also, verify that your post script works fine when executed by hand.
exec replaces the current shell process, and doesn't start a new one. So after the exec command, your shell stops.
The purpose of your particular exec command eludes me by the way ... So just remove it and you should be fine.
you'd better to exec a 'cd' first, to a directory where you really want the shell to execute.
i'm not sure the path SVN will have when running this, but of course your script have potential privilege problems