28

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?

Christian Studer
  • 22,792
  • 6
  • 39
  • 70

4 Answers4

52
$command =  'yourcommand' . ' > /dev/null 2>&1 & echo $!; ';

$pid = exec($command, $output);

var_dump($pid);
Boris Guéry
  • 45,611
  • 7
  • 48
  • 85
  • What's the meaning of " > /dev/null 2>&1 & echo $!; " – andho Sep 25 '09 at 08:48
  • it means 'send all the output to the null device, which means that all the output will be available in the stdout, and echo $! simply echo the pid, which is captured by the script – Boris Guéry Sep 25 '09 at 15:33
  • This also means that the command will run in the background (which would be what you want if you want the pid), and so your php script will not wait for the command to finish executing – Tofandel Jun 15 '17 at 20:50
  • this is useless when it doesn't wait for command to finish, -1 from me – Flash Thunder Jun 19 '19 at 15:09
  • If you want to wait for the command to finish remove & as & stands for asynchronous mode – jimver04 Jul 03 '19 at 08:38
  • @FlashThunder there are many scenarios where you want **not to wait** for the script to finish for example , you are providing an interface where you need to dump all 200K records to a CSV file and show the progress along with the export you would definitely want to run it in background and involve some table to calculate the current running export progress and update the progress bar in the frontend, So before you call a solution useless, please review all the use cases first. – Reborn Oct 31 '19 at 12:11
  • @jimver04 I think technically the required modification would be to change `&` to `;` – rinogo Aug 25 '20 at 02:36
23

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;
Community
  • 1
  • 1
Mark Amery
  • 110,735
  • 57
  • 354
  • 402
2

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.

Community
  • 1
  • 1
Mikhail Vasin
  • 1,843
  • 1
  • 19
  • 27
0

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.

TheKarateKid
  • 600
  • 9
  • 18