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






Можете ли вы организовать отдельный процесс, а затем запустить свою копию в фоновом режиме? Давненько я не использовал PHP, но функция pcntl-вилка выглядит многообещающей.
Это не дает ответа на вопрос. Чтобы критиковать или запрашивать разъяснения у автора, оставьте комментарий под его сообщением.
Спасибо - комментариев не было в '08, но я буду иметь это в виду :)
Предполагая, что это работает на машине 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; // так что я могу убить потом ничего не дает.
Можно ли запускать установку sudo без запроса пароля? Любые команды, требующие ввода данных пользователем, работать не будут.
Да, он отключен, режим без пароля включен, но все равно не работает. Например: #Defaults requiretty root ALL = (ALL) ALL apache ALL = (ALL) NOPASSWD: ALL
См. Небольшой пример, в котором я объясняю все детали, но получаю ошибку. Например: gist.github.com/1010541
Этот stackoverflow.com/questions/5367261/… объясняет, как сделать то же самое под Windows.
Я использовал это, но немного обновил, поэтому мне не нужно было записывать PID в файл. Поэтому я использую этот формат: <code> exec (sprintf ("$ s> $ s 2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </code> Полученный PID процесса находится в $ pidArr [0]
@Kaiesh: это должно быть "echo $!"
@brunobg Да ты прав! Спасибо за исправление! Опечатка :-)
Если вы хотите обрабатывать коды выхода, просто замените первый «%s» на «(%s; printf \"\n$?\")». Это поместит код выхода команды в последнюю строку журнала.
Кто-нибудь знает, как использовать sprintf на Ubuntu 12.04? Он недоступен, и, похоже, я не нашел хороших решений в моем поиске в Google.
@BrianRuff sprintf() в контексте выше - это встроенная функция PHP. Он доступен во всех установках PHP, но не на уровне ОС.
Кайеш допустил еще одну опечатку: «$» вместо «%». Исправленная версия: exec (sprintf ("% s>% s 2> & 1 & echo $!", $ Cmd, $ outputfile), $ pidArr)
Мне кажется, это очень сильно ломает. В итоге я использовал это: symcbean.blogspot.co.uk/2010/02/…
Интересный ответ, но я думаю, что в конечном итоге PHP - неподходящий инструмент для работы в данном случае. Этот пример будет работать только с командами bash - Использование потоков (и, возможно, очереди для обработки рабочей нагрузки для некоторого количества потоков) работает в Python и позволяет запускать любую процедуру функции. См. pymotw.com/2/Queue
Pid мог быть занят другим процессом. В этом случае isRunning () вернет true - в то же время процесс действительно остановился.
Превратил этот ответ в простой пакет композитора. Вы можете найти его на github: github.com/diversen/background-job
когда вы используете 'ps h -o pid <pid>', вы получите результат только в том случае, если процесс все еще жив. так что нет необходимости в preg_split и т.п.
Вы должны использовать escapeshellcmd() или escapeshellarg() для своих переменных, прежде чем использовать их в exec()!
Напишите процесс как серверный скрипт на любом удобном языке (php / bash / perl / etc), а затем вызовите его из функций управления процессом в вашем php-скрипте.
Функция, вероятно, определяет, используется ли стандартный io в качестве выходного потока, и если это так, то это установит возвращаемое значение ... если нет, то он завершится
proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );
Я быстро протестировал это из командной строки, используя в качестве команды «sleep 25s», и это сработало отлично.
Это единственный способ заставить его работать на centos. Спасибо!
Спасибо ! Существует несовместимость PHP и BASH. Если вы запустите новую систему, она не сможет запуститься в фоновом режиме с помощью system () или exec (). Дескрипторы файлов, кажется, застревают, даже если вы перенаправляете. Ваш метод сработал. другая альтернатива - использовать старый двоичный файл bash для PHP, но это безумие
Я просто хотел бы добавить очень простой пример для тестирования этой функции в 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" />
Действительно плохое решение. Если пользователь покинет страницу, процесс будет прерван.
«невидимое изображение» и его ссылка отображаются в исходном коде страницы.
Вместо запуска фонового процесса, как насчет создания файла триггера и того, чтобы планировщик, такой как cron или autosys, периодически выполнял сценарий, который ищет файлы триггеров и воздействует на них? Триггеры могут содержать инструкции или даже необработанные команды (еще лучше, просто сделайте это сценарием оболочки).
Вы можете попробовать добавить это в свою команду
>/dev/null 2>/dev/null &
например.
shell_exec('service named reload >/dev/null 2>/dev/null &');
Я имел в виду> / dev / null 2> / dev / null &
У меня это работает, и я буду использовать make-файл для чего-нибудь на сервере, спасибо! (например, сделать, перезапустить)
Это намного проще, чем принятый ответ! (Пока вам не нужно ничего более сложного, например, проверить, выполняется ли процесс.)
Вы можете попробовать систему очередей, например Resque. Затем вы можете сгенерировать задание, которое обрабатывает информацию и довольно быстро возвращает изображение с «обработкой». Однако при таком подходе вы не узнаете, когда он будет закончен.
Это решение предназначено для крупномасштабных приложений, где вы не хотите, чтобы ваши передние машины выполняли тяжелую работу, чтобы они могли обрабатывать запросы пользователей. Поэтому он может работать, а может и не работать с физическими данными, такими как файлы и папки, но для обработки более сложной логики или других асинхронных задач (например, новых сообщений о регистрации) его хорошо иметь и очень масштабируемо.
Ну, я нашел немного более быструю и простую версию для использования
shell_exec('screen -dmS $name_of_screen $command');
и это работает.
@ Alpha.Dev Я тестировал его только на Linux, я не знаю, будет ли он работать на другой системе.
Если вы используете PHP, есть более простой способ сделать это с помощью pcntl_fork:
Сценарии 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
P.s. Ссылка на MSDN не работает
@ Alph.Dev: Кажется, что документ msdn временно удален. Он содержал больше примеров и объяснений того, как использовать WScript.Shell.
@ Alph.Dev: Для класса COM не найден, это совсем другое дело. Проверьте это в Google. Вы также можете проверить это php.net/manual/en/com.installation.php
@ Alph.Dev, пожалуйста, проверьте technet.microsoft.com/en-us/library/ee156605.aspx для получения обновленной информации о функции Run ()
Вот функция для запуска фонового процесса в 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 зависнет (возможно, до завершения процесса).
Этот stackoverflow.com/questions/5367261/… объясняет, как это сделать под Windows.