Php выполнить фоновый процесс

Мне нужно выполнить копию каталога при действии пользователя, но каталоги довольно большие, поэтому я хотел бы иметь возможность выполнять такое действие, не зная, сколько времени потребуется для завершения копирования.

Любые предложения будут высоко ценится.

Этот stackoverflow.com/questions/5367261/… объясняет, как это сделать под Windows.

shealtiel 10.08.2011 23:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
271
1
243 730
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

Можете ли вы организовать отдельный процесс, а затем запустить свою копию в фоновом режиме? Давненько я не использовал PHP, но функция pcntl-вилка выглядит многообещающей.

Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением.

Rizier123 05.02.2015 20:08

Спасибо - комментариев не было в '08, но я буду иметь это в виду :)

Matt Sheppard 07.02.2015 10:38
Ответ принят как подходящий

Предполагая, что это работает на машине Linux, я всегда обращался с этим следующим образом:

exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));

Это запускает команду $cmd, перенаправляет вывод команды на $outputfile и записывает идентификатор процесса в $pidfile.

Это позволяет вам легко отслеживать, что делает процесс и работает ли он.

function isRunning($pid){
    try{
        $result = shell_exec(sprintf("ps %d", $pid));
        if ( count(preg_split("/\n/", $result)) > 2){
            return true;
        }
    }catch(Exception $e){}

    return false;
}

Пробовал твой образец. Но, к сожалению, в этом случае это не сработало. $ cmd = "sudo -u sun mplayer /tmp/demo.wav>/dev/null &"; exec (sprintf ("% s>% s 2> & 1 & echo $! >>% s", $ cmd, $ outputfile, $ pidfile)); echo $ pidfile; // так что я могу убить потом ничего не дает.

user285594 06.06.2011 19:01

Можно ли запускать установку sudo без запроса пароля? Любые команды, требующие ввода данных пользователем, работать не будут.

Mark Biek 06.06.2011 19:39

Да, он отключен, режим без пароля включен, но все равно не работает. Например: #Defaults requiretty root ALL = (ALL) ALL apache ALL = (ALL) NOPASSWD: ALL

user285594 06.06.2011 19:54

См. Небольшой пример, в котором я объясняю все детали, но получаю ошибку. Например: gist.github.com/1010541

user285594 06.06.2011 20:09

Этот stackoverflow.com/questions/5367261/… объясняет, как сделать то же самое под Windows.

shealtiel 10.08.2011 23:30

Я использовал это, но немного обновил, поэтому мне не нужно было записывать PID в файл. Поэтому я использую этот формат: <code> exec (sprintf ("$ s> $ s 2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </code> Полученный PID процесса находится в $ pidArr [0]

Kaiesh 28.03.2013 06:13

@Kaiesh: это должно быть "echo $!"

brunobg 17.06.2013 23:36

@brunobg Да ты прав! Спасибо за исправление! Опечатка :-)

Kaiesh 19.06.2013 12:07

Если вы хотите обрабатывать коды выхода, просто замените первый «%s» на «(%s; printf \"\n$?\")». Это поместит код выхода команды в последнюю строку журнала.

flu 16.09.2013 17:29

Кто-нибудь знает, как использовать sprintf на Ubuntu 12.04? Он недоступен, и, похоже, я не нашел хороших решений в моем поиске в Google.

Brian Ruff 19.12.2013 21:34

@BrianRuff sprintf() в контексте выше - это встроенная функция PHP. Он доступен во всех установках PHP, но не на уровне ОС.

Michael Berkowski 10.05.2014 17:12

Кайеш допустил еще одну опечатку: «$» вместо «%». Исправленная версия: exec (sprintf ("% s>% s 2> & 1 & echo $!", $ Cmd, $ outputfile), $ pidArr)

Yuri Gor 13.05.2014 19:05

Мне кажется, это очень сильно ломает. В итоге я использовал это: symcbean.blogspot.co.uk/2010/02/…

LeonardChallis 25.07.2014 14:25

Интересный ответ, но я думаю, что в конечном итоге PHP - неподходящий инструмент для работы в данном случае. Этот пример будет работать только с командами bash - Использование потоков (и, возможно, очереди для обработки рабочей нагрузки для некоторого количества потоков) работает в Python и позволяет запускать любую процедуру функции. См. pymotw.com/2/Queue

NoBugs 15.12.2014 08:23

Pid мог быть занят другим процессом. В этом случае isRunning () вернет true - в то же время процесс действительно остановился.

frodeborli 09.02.2016 16:57

Превратил этот ответ в простой пакет композитора. Вы можете найти его на github: github.com/diversen/background-job

dennis 18.05.2016 11:46

когда вы используете 'ps h -o pid <pid>', вы получите результат только в том случае, если процесс все еще жив. так что нет необходимости в preg_split и т.п.

emfi 05.07.2017 23:14

Вы должны использовать escapeshellcmd() или escapeshellarg() для своих переменных, прежде чем использовать их в exec()!

TheStoryCoder 09.04.2019 21:36

Напишите процесс как серверный скрипт на любом удобном языке (php / bash / perl / etc), а затем вызовите его из функций управления процессом в вашем php-скрипте.

Функция, вероятно, определяет, используется ли стандартный io в качестве выходного потока, и если это так, то это установит возвращаемое значение ... если нет, то он завершится

proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );

Я быстро протестировал это из командной строки, используя в качестве команды «sleep 25s», и это сработало отлично.

(Ответ найден здесь)

Это единственный способ заставить его работать на centos. Спасибо!

Deac Karns 19.11.2012 23:58

Спасибо ! Существует несовместимость PHP и BASH. Если вы запустите новую систему, она не сможет запуститься в фоновом режиме с помощью system () или exec (). Дескрипторы файлов, кажется, застревают, даже если вы перенаправляете. Ваш метод сработал. другая альтернатива - использовать старый двоичный файл bash для PHP, но это безумие

John 05.04.2015 03:51

Я просто хотел бы добавить очень простой пример для тестирования этой функции в Windows:

Создайте следующие два файла и сохраните их в веб-каталоге:

foreground.php:

<?php

ini_set("display_errors",1);
error_reporting(E_ALL);

echo "<pre>loading page</pre>";

function run_background_process()
{
    file_put_contents("testprocesses.php","foreground start time = " . time() . "\n");
    echo "<pre>  foreground start time = " . time() . "</pre>";

    // output from the command must be redirected to a file or another output stream 
    // http://ca.php.net/manual/en/function.exec.php

    exec("php background.php > testoutput.php 2>&1 & echo $!", $output);

    echo "<pre>  foreground end time = " . time() . "</pre>";
    file_put_contents("testprocesses.php","foreground end time = " . time() . "\n", FILE_APPEND);
    return $output;
}

echo "<pre>calling run_background_process</pre>";

$output = run_background_process();

echo "<pre>output = "; print_r($output); echo "</pre>";
echo "<pre>end of page</pre>";
?>

background.php:

<?
file_put_contents("testprocesses.php","background start time = " . time() . "\n", FILE_APPEND);
sleep(10);
file_put_contents("testprocesses.php","background end time = " . time() . "\n", FILE_APPEND);
?>

Дайте IUSR разрешение на запись в каталог, в котором вы создали указанные выше файлы.

Дайте IUSR разрешение на ЧТЕНИЕ и ВЫПОЛНЕНИЕ C: \ Windows \ System32 \ cmd.exe

Нажмите foreground.php из веб-браузера.

Следующее должно быть отображено в браузере с текущими отметками времени и локальным ресурсом # в выходном массиве:

loading page
calling run_background_process
  foreground start time = 1266003600
  foreground end time = 1266003600
output = Array
(
    [0] => 15010
)
end of page

Вы должны увидеть testoutput.php в том же каталоге, в котором были сохранены указанные выше файлы, и он должен быть пустым.

Вы должны увидеть testprocesses.php в том же каталоге, в котором были сохранены вышеуказанные файлы, и он должен содержать следующий текст с текущими отметками времени:

foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610

Если вам нужно просто сделать что-то в фоновом режиме, не дожидаясь завершения PHP-страницы, вы можете использовать другой (фоновый) PHP-скрипт, который "вызывается" командой wget. Этот фоновый сценарий PHP будет выполняться с привилегиями, конечно же, как и любой другой сценарий PHP в вашей системе.

Вот пример использования wget в Windows из пакетов gnuwin32.

Фоновый код (файл test-proc-bg.php) в качестве примера ...

sleep(5);   // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file

Сценарий переднего плана, вызывающий ...

$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);

Вы должны использовать popen / pclose, чтобы это работало правильно.

Параметры wget:

-q    keeps wget quiet.
-O -  outputs to stdout.
-b    works on background

Я знаю, что это пост 100-летней давности, но в любом случае подумал, что он может быть кому-то полезен. Вы можете разместить где-нибудь на странице невидимое изображение, указывающее на URL-адрес, который должен работать в фоновом режиме, например:

<img src = "run-in-background.php" border = "0" alt = "" width = "1" height = "1" />

Действительно плохое решение. Если пользователь покинет страницу, процесс будет прерван.

Sergey P. aka azure 28.03.2012 15:28

«невидимое изображение» и его ссылка отображаются в исходном коде страницы.

tony gil 06.02.2013 18:51

Вместо запуска фонового процесса, как насчет создания файла триггера и того, чтобы планировщик, такой как cron или autosys, периодически выполнял сценарий, который ищет файлы триггеров и воздействует на них? Триггеры могут содержать инструкции или даже необработанные команды (еще лучше, просто сделайте это сценарием оболочки).

Вы можете попробовать добавить это в свою команду

>/dev/null 2>/dev/null &

например.

shell_exec('service named reload >/dev/null 2>/dev/null &');

Я имел в виду> / dev / null 2> / dev / null &

wlf 14.08.2012 18:43

У меня это работает, и я буду использовать make-файл для чего-нибудь на сервере, спасибо! (например, сделать, перезапустить)

Chu-Siang Lai 24.01.2014 07:17

Это намного проще, чем принятый ответ! (Пока вам не нужно ничего более сложного, например, проверить, выполняется ли процесс.)

Sean the Bean 01.02.2018 00:40

Вы можете попробовать систему очередей, например Resque. Затем вы можете сгенерировать задание, которое обрабатывает информацию и довольно быстро возвращает изображение с «обработкой». Однако при таком подходе вы не узнаете, когда он будет закончен.

Это решение предназначено для крупномасштабных приложений, где вы не хотите, чтобы ваши передние машины выполняли тяжелую работу, чтобы они могли обрабатывать запросы пользователей. Поэтому он может работать, а может и не работать с физическими данными, такими как файлы и папки, но для обработки более сложной логики или других асинхронных задач (например, новых сообщений о регистрации) его хорошо иметь и очень масштабируемо.

Ну, я нашел немного более быструю и простую версию для использования

shell_exec('screen -dmS $name_of_screen $command'); 

и это работает.

@ Alpha.Dev Я тестировал его только на Linux, я не знаю, будет ли он работать на другой системе.

Morfidon 04.01.2016 17:45

Если вы используете PHP, есть более простой способ сделать это с помощью pcntl_fork:

http://www.php.net/manual/en/function.pcntl-fork.php

Сценарии PHP не похожи на другие языки разработки настольных приложений. В языках настольных приложений мы можем настроить потоки демонов для запуска фонового процесса, но в PHP процесс происходит, когда пользователь запрашивает страницу. Однако можно установить фоновое задание, используя функциональность задания cron сервера, которую запускает скрипт php.

Я активно использую fast_cgi_finish_request()

В сочетании с закрытием и register_shutdown_function ()

$message ='job executed';
$backgroundJob = function() use ($message) {
     //do some work here
    echo $message;
}

Затем зарегистрируйте это закрытие для выполнения перед выключением.

register_shutdown_function($backgroundJob);

Наконец, когда ответ был отправлен клиенту, вы можете закрыть соединение с клиентом и продолжить работу с процессом PHP:

fast_cgi_finish_request();

Закрытие будет выполнено после fast_cgi_finish_request.

Сообщение $ не будет отображаться в любое время. И вы можете зарегистрировать сколько угодно закрытий, но позаботьтесь о времени выполнения скрипта. Это будет работать только в том случае, если PHP работает как модуль Fast CGI (это было правильно ?!)

Для тех из нас, кто использует Окна, посмотрите на это:

Ссылка: http://php.net/manual/en/function.exec.php#43917

I too wrestled with getting a program to run in the background in Windows while the script continues to execute. This method unlike the other solutions allows you to start any program minimized, maximized, or with no window at all. llbra@phpbrasil's solution does work but it sometimes produces an unwanted window on the desktop when you really want the task to run hidden.

запустить свернутый Notepad.exe в фоновом режиме:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("notepad.exe", 7, false); 
?> 

запустить невидимую в фоновом режиме команду оболочки:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false); 
?> 

запустите MSPaint в развернутом виде и дождитесь, пока вы его закроете, прежде чем продолжить скрипт:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("mspaint.exe", 3, true); 
?> 

Для получения дополнительной информации о методе Run () перейдите по ссылке: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

Отредактированный URL:

Вместо этого перейдите к https://technet.microsoft.com/en-us/library/ee156605.aspx, поскольку указанная выше ссылка больше не существует.

Что нужно сделать, чтобы запустить этот код? Я получаю Fatal error: Class 'COM' not found

Slava 04.01.2016 17:38

P.s. Ссылка на MSDN не работает

Slava 04.01.2016 17:41

@ Alph.Dev: Кажется, что документ msdn временно удален. Он содержал больше примеров и объяснений того, как использовать WScript.Shell.

Jimmy Ilenloa 17.02.2016 09:55

@ Alph.Dev: Для класса COM не найден, это совсем другое дело. Проверьте это в Google. Вы также можете проверить это php.net/manual/en/com.installation.php

Jimmy Ilenloa 17.02.2016 09:58

@ Alph.Dev, пожалуйста, проверьте technet.microsoft.com/en-us/library/ee156605.aspx для получения обновленной информации о функции Run ()

Jimmy Ilenloa 01.05.2018 00:05

Вот функция для запуска фонового процесса в PHP. Наконец, после долгого чтения и тестирования различных подходов и параметров был создан тот, который действительно работает и в Windows.

function LaunchBackgroundProcess($command){
  // Run command Asynchroniously (in a separate thread)
  if (PHP_OS=='WINNT' || PHP_OS=='WIN32' || PHP_OS=='Windows'){
    // Windows
    $command = 'start "" '. $command;
  } else {
    // Linux/UNIX
    $command = $command .' /dev/null &';
  }
  $handle = popen($command, 'r');
  if ($handle!==false){
    pclose($handle);
    return true;
  } else {
    return false;
  }
}

Примечание 1. В Windows не используйте параметр /B, как это предлагается в другом месте. Он заставляет процесс запускать то же окно консоли, что и сама команда start, в результате чего процесс обрабатывается синхронно. Чтобы запустить процесс в отдельном потоке (асинхронно), не используйте /B.

Примечание 2: пустые двойные кавычки после start "" необходимы, если команда является путем в кавычках. Команда start интерпретирует первый указанный в кавычках параметр как заголовок окна.

Используйте эту функцию для запуска вашей программы в фоновом режиме. Он кроссплатформенный и полностью настраиваемый.

<?php
function startBackgroundProcess(
    $command,
    $stdin = null,
    $redirectStdout = null,
    $redirectStderr = null,
    $cwd = null,
    $env = null,
    $other_options = null
) {
    $descriptorspec = array(
        1 => is_string($redirectStdout) ? array('file', $redirectStdout, 'w') : array('pipe', 'w'),
        2 => is_string($redirectStderr) ? array('file', $redirectStderr, 'w') : array('pipe', 'w'),
    );
    if (is_string($stdin)) {
        $descriptorspec[0] = array('pipe', 'r');
    }
    $proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
    if (!is_resource($proc)) {
        throw new \Exception("Failed to start background process by command: $command");
    }
    if (is_string($stdin)) {
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }
    if (!is_string($redirectStdout)) {
        fclose($pipes[1]);
    }
    if (!is_string($redirectStderr)) {
        fclose($pipes[2]);
    }
    return $proc;
}

Обратите внимание, что после запуска команды по умолчанию эта функция закрывает stdin и stdout запущенного процесса. Вы можете перенаправить вывод процесса в какой-либо файл с помощью аргументов $ redirectStdout и $ redirectStderr.

Примечание для пользователей Windows:
Вы не можете перенаправить stdout / stderr на nul следующим образом:

startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');

Однако вы можете сделать это:

startBackgroundProcess('ping yandex.com >nul 2>&1');

Примечания для пользователей * nix:

1) Используйте команду оболочки exec, если вы хотите получить фактический PID:

$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));

2) Используйте аргумент $ stdin, если вы хотите передать некоторые данные на вход вашей программы:

startBackgroundProcess('cat > input.txt', "Hello world!\n");

Рабочее решение как для Windows, так и для Linux. Узнайте больше о Моя страница на github.

function run_process($cmd,$outputFile = '/dev/null', $append = false){
                    $pid=0;
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        $cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
                        $handle = popen("start /B ". $cmd, "r");
                        $read = fread($handle, 200); //Read the output 
                        $pid=substr($read,strpos($read,'=')+1);
                        $pid=substr($pid,0,strpos($pid,';') );
                        $pid = (int)$pid;
                        pclose($handle); //Close
                }else{
                    $pid = (int)shell_exec(sprintf('%s %s %s 2>&1 & echo $!', $cmd, ($append) ? '>>' : '>', $outputFile));
                }
                    return $pid;
            }
            function is_process_running($pid){
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        //tasklist /FI "PID eq 6480"
                    $result = shell_exec('tasklist /FI "PID eq '.$pid.'"' );
                    if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                        return true;
                    }
                }else{
                    $result = shell_exec(sprintf('ps %d 2>&1', $pid));
                    if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) {
                        return true;
                    }
                }
                return false;
            }
            function stop_process($pid){
                    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                            $result = shell_exec('taskkill /PID '.$pid );
                        if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                            return true;
                        }
                    }else{
                            $result = shell_exec(sprintf('kill %d 2>&1', $pid));
                        if (!preg_match('/No such process/', $result)) {
                            return true;
                        }
                    }
            }

Благодаря этот ответ: идеальным инструментом для запуска фонового процесса будет Компонент процесса Symfony, который основан на функциях proc_*, но его намного проще использовать. См. его документация для получения дополнительной информации.

Если вы хотите выполнить фоновый процесс через PHP, направьте вывод команды в /dev/null и добавьте & в конец команды.

exec("bg_process > /dev/null &");

Обратите внимание, что вы можете нет использовать параметр $outputexec(), иначе PHP зависнет (возможно, до завершения процесса).

Другие вопросы по теме