How can I invoke an external shell script (Or alternatively an external PHP script) from PHP itself and get its process ID within the same script?
$command = 'yourcommand' . ' > /dev/null 2>&1 & echo $!; ';
$pid = exec($command, $output);
var_dump($pid);
If you want to do this strictly using tools PHP gives you, rather than Unix-specific wizardry, you can do so with proc_open and proc_get_status, although the need to pass a descriptor spec into proc_open makes it unpleasantly verbose to use:
<?php
$descriptorspec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
];
$proc = proc_open('yourcommand', $descriptorspec, $pipes);
$proc_details = proc_get_status($proc);
$pid = $proc_details['pid'];
echo $pid;
For a cross-platform solution, check out symfony/process.
use Symfony\Component\Process\Process;
$process = new Process('sleep 100');
$process->start();
var_dump($process->getPid());
After you install symfony/process with composer (composer require symfony/process), you may need to update autoloading info with composer dump-autoload and then require the autoload with require __DIR__ . '/vendor/autoload.php';.
Notice also that you can get PID of a running process only. Refer to the documentation for API details.
What ended up working for me is using pgrep to get the PID of the command (or process name) executed after calling exec() in PHP.
exec($command);
$pid = exec("pgrep $command");
This will work for launching background processes too. However, you must remember to pipe the program's output to /dev/null or else PHP will hang. Also, when calling pgrep you can't include the pipe portion of the command:
$command = "bg_process -o someOption";
exec($command + " > /dev/null &"); //Separate the pipe and '&' from the command
$pid = exec("pgrep $command");
Note that if the system has multiple processes launched with the same exact command, it will return the PIDs of all processes which match the command given to pgrep. If you only pass in a process name, it will return all PIDs with that process name.
Related
I try to invoke an app inside php:
ob_start();
passthru("(cd /opt/server/TrackServer/release && ./TrackServer& ) && pidof TrackServer");
$pid = ob_get_clean();
var_dump($pid);
exit;
The goal is to run TrackServer within its path and to get it's process id so I can close it after I do some test.
When I run the command in terminal:
(cd /opt/server/TrackServer/release && nohup ./TrackServer&) && pidof TrackServer
I get correct pid returned but in php the command stops and doesn't go further, the TrackServer is started and running but I have to kill it from terminal to unblock the php script, after killing the process the php script prints the correct pid for the process I've just closed from terminal.
Why the command stops?
Is there a way to make it run in php the way I'am trying to run it (without forking to a new thread)?
From the passthru manual page: The passthru() function is similar to the exec() function in that it executes a command.
What this means is that you can't execute your command line directly, as this runs several commands and relies on the shell to implement backgrounding and subshells as needed.
Try this instead:
passthru("/bin/bash -c 'cd /opt/server/TrackServer/release && nohup ./TrackServer& && pidof TrackServer'");
EDIT:
I found a working solution:
ob_start();
passthru("/bin/bash -c 'cd /opt/server/TrackServer/release && nohup ./TrackServer&' > /dev/null 2>&1 &");
passthru("pidof TrackServer");
$pid = ob_get_clean();
The command was stopping because:
Run multiple exec commands at once (But wait for the last one to finish)
PHP's exec function will always wait for a response from your
execution. However you can send the stdout & stderror of the process
to /dev/null (on unix) and have these all the scripts executed almost
instantly.
We have a tool which executes the PHP interactive shell like this:
$descriptorSpec = array(
0 => STDIN,
1 => STDOUT,
2 => STDERR
);
$prependFile = __DIR__ . '/../../../../../res/dev/console_auto_prepend.php';
$exec = 'php -a -d auto_prepend_file=' . escapeshellarg($prependFile);
$pipes = array();
proc_open($exec, $descriptorSpec, $pipes);
The trick with auto_prepend_file unfortunately causes issues with autoloading on PHP 5.3. We found out that everyting works well when we include the file inside the interactive shell:
$ php -a
Interactive shell
php > include "myproject/res/dev/console_auto_prepend.php";
Autoloader initialized.
What we want to do is the following:
execute php interactive shell via proc_open
send the include line to the interactive shell
hand over the controll to the user input
Is there any way to do this?
Untested idea:
Created a new input pipe
Open the PHP process (php -a) with that input pipe, stdout and stderr could be connected to the system pipes
Inject the include command (write to the input pipe)
In a loop read from the STDIN and write to our new input pipe (which is connected to the php -a)
The project ended up in using a custom PHP shell (psysh)
I am using this code on Ubuntu 13.04,
$cmd = "sleep 20 &> /dev/null &";
exec($cmd, $output);
Although it actually sits there for 20 seconds and waits :/ usually it works fine when using & to send a process to the background, but on this machine php just won't do it :/
What could be causing this??
Try
<?PHP
$cmd = '/bin/sleep';
$args = array('20');
$pid=pcntl_fork();
if($pid==0)
{
posix_setsid();
pcntl_exec($cmd,$args,$_ENV);
// child becomes the standalone detached process
}
echo "DONE\n";
I tested it for it works.
Here you first fork the php process and then exceute your task.
Or if the pcntl module is not availabil use:
<?PHP
$cmd = "sleep 20 &> /dev/null &";
exec('/bin/bash -c "' . addslashes($cmd) . '"');
The REASON this doesn't work is that exec() executes the string you're passing into it. Since & is interpreted by the shell as "execute in the background", but you don't execute a shell in your exec call, the & is just passed along with 20 to the /bin/sleep executable - which probably just ignores that.
The same applies to the redirection of output, since that is also parsed by the shell, not in exec.
So, you either need to find a way to fork your process (as described above), or a way to run the subprocess as a shell.
My workaround to do this on ubuntu 13.04 with Apache2 and any version of PHP:
libssh2-php, I just used nohup $cmd & inside a local SSH session using PHP and it ran it just fine the background, of course this requires putting certain security protocols in place, such as enabling SSH access for the webserver user, so it would have exec-like permissions then only allowing localhost to login to the webserver ssh account.
From exec() manual page:
If a program is started with this function, in order for it to
continue running in the background, the output of the program must be
redirected to a file or another output stream. Failing to do so will
cause PHP to hang until the execution of the program ends.
So, it seems i should redirect output to something and add ampersand under Linux, to make my script truly running in background.
How i'm supposed to "translate" /dev/null and & for Windows? Any chance to use php:// to make it OS safe?
exec('php ' . __DIR__ . '/script.php > /dev/null &');
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
I have a command I want to run, but I do not want PHP to sit and wait for the result.
<?php
echo "Starting Script";
exec('run_baby_run');
echo "Thanks, Script is running in background";
?>
Is it possible to have PHP not wait for the result.. i.e. just kick it off and move along to the next command.
I cant find anything, and not sure its even possible. The best I could find was someone making a CRON job to start in a minute.
From the documentation:
In order to execute a command and have it not hang your PHP script while
it runs, the program you run must not output back to PHP. To do this,
redirect both stdout and stderr to /dev/null, then background it.
> /dev/null 2>&1 &
In order to execute a command and have
it spawned off as another process that
is not dependent on the Apache thread
to keep running (will not die if
somebody cancels the page) run this:
exec('bash -c "exec nohup setsid your_command > /dev/null 2>&1 &"');
You can run the command in the background by adding a & at the end of it as:
exec('run_baby_run &');
But doing this alone will hang your script because:
If a program is started with exec function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
So you can redirect the stdout of the command to a file, if you want to see it later or to /dev/null if you want to discard it as:
exec('run_baby_run > /dev/null &');
This uses wget to notify a URL of something without waiting.
$command = 'wget -qO- http://test.com/data=data';
exec('nohup ' . $command . ' >> /dev/null 2>&1 & echo $!', $pid);
This uses ls to update a log without waiting.
$command = 'ls -la > content.log';
exec('nohup ' . $command . ' >> /dev/null 2>&1 & echo $!', $pid);
I know this question has been answered but the answers i found here didn't work for my scenario ( or for Windows ).
I am using windows 10 laptop with PHP 7.2 in Xampp v3.2.4.
$command = 'php Cron.php send_email "'. $id .'"';
if ( substr(php_uname(), 0, 7) == "Windows" )
{
//windows
pclose(popen("start /B " . $command . " 1> temp/update_log 2>&1 &", "r"));
}
else
{
//linux
shell_exec( $command . " > /dev/null 2>&1 &" );
}
This worked perfectly for me.
I hope it will help someone with windows. Cheers.
There are two possible ways to implement it.
The easiest way is direct result to dev/null
exec("run_baby_run > /dev/null 2>&1 &");
But in case you have any other operations to be performed you may consider ignore_user_abort
In this case the script will be running even after you close connection.
"exec nohup setsid your_command"
the nohup allows your_command to continue even though the process that launched may terminate first. If it does, the the SIGNUP signal will be sent to your_command causing it to terminate (unless it catches that signal and ignores it).
On Windows, you may use the COM object:
if(class_exists('COM')) {
$shell = new COM('WScript.Shell');
$shell->Run($cmd, 1, false);
}
else {
exec('nohup ' . $cmd . ' 2>&1 &');
}