Мені потрібно виконати копію каталогів після дії користувача, але каталоги досить великі, тому я хотів би мати можливість виконувати таку дію, не знаючи про час, який потрібен копію для завершення копії.
Будь-які пропозиції були б вдячні.
Мені потрібно виконати копію каталогів після дії користувача, але каталоги досить великі, тому я хотів би мати можливість виконувати таку дію, не знаючи про час, який потрібен копію для завершення копії.
Будь-які пропозиції були б вдячні.
Відповіді:
Припускаючи, що це працює на машині 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;
}
Запишіть процес у вигляді сервера на будь-якій мові (php / bash / perl / тощо), а потім викликайте його з функцій управління процесом у вашому скрипті php.
Функція, ймовірно, визначає, чи використовується стандартний io як вихідний потік, і якщо саме тоді буде встановлено повернене значення.
proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );
Я швидко перевірив це з командного рядка, використовуючи команду "sleep 25s" як команда, і це спрацювало як шарм.
Ви можете спробувати додати це до своєї команди
>/dev/null 2>/dev/null &
напр.
shell_exec('service named reload >/dev/null 2>/dev/null &');
Я просто хотів би додати дуже простий приклад тестування цієї функціональності в 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 у тому самому каталозі, де були збережені вищезазначені файли, і він повинен бути порожнім
Ви повинні побачити testprocess.php у тому самому каталозі, де були збережені вищевказані файли, і він повинен містити наступний текст із поточними часовими позначками:
foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610
Якщо вам потрібно просто зробити щось у фоновому режимі без сторінки PHP, яка чекає його завершення, ви можете використовувати інший (фоновий) PHP-скрипт, який "викликається" командою wget. Цей фоновий скрипт PHP буде виконаний, звичайно, як і будь-який інший скрипт PHP у вашій системі.
Ось приклад в Windows, що використовує wget з gnuwin32 пакетів.
Фоновий код (тест файлу-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
Ось функція запуску фонового процесу в 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, але функція pcntl-fork виглядає багатообіцяючо.
Використовуйте цю функцію для запуску програми у фоновому режимі. Він крос-платформний і повністю настроюється.
<?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 shell, якщо ви хочете отримати фактичний 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");
Ви можете спробувати систему черги на зразок Resque . Потім ви можете створити завдання, яке обробляє інформацію та досить швидко повертається із зображенням "обробкою". При такому підході ви не знатимете, коли він буде закінчений.
Це рішення призначене для більш масштабних застосувань, де ви не хочете, щоб ваші передні машини виконували важкі підйоми, щоб вони могли обробляти запити користувачів. Таким чином, він може або не може працювати з фізичними даними, такими як файли та папки, але для обробки більш складної логіки або інших асинхронних завдань (тобто нових повідомлень про реєстрацію) це приємно мати і дуже масштабувати.
Робоче рішення як для 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 Process Component , який базується на proc_*функціях, але це набагато простіше у використанні. Для отримання додаткової інформації дивіться його документацію .
Замість того, щоб ініціювати фоновий процес, що робити із створенням тригерного файлу та створенням планувальника, наприклад, cron або autosys, періодично виконувати скрипт, який шукає і діє на тригерні файли? Тригери можуть містити вказівки або навіть необроблені команди (ще краще, просто зробіть це сценарієм оболонки).
Якщо використовується PHP, є набагато простіший спосіб зробити це за допомогою pcntl_fork:
Я дуже використовую 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 працює як модуль швидкого CGI (правильно?)
PHP-скрипт не схожий на інші мови, що розробляють настільні програми. У мовах додатків для настільних комп'ютерів ми можемо встановити демонні потоки для запуску фонового процесу, але в PHP процес виникає, коли запит користувача на сторінку. Однак можна встановити фонове завдання, використовуючи функцію завдання cron сервера, на якій запущений php-скрипт.
Для тих, хто використовує Windows , подивіться на це:
Довідка: http://php.net/manual/en/function.exec.php#43917
Я надто боровся з тим, щоб програма запускалася у фоновому режимі в Windows, поки сценарій продовжує виконуватись. Цей метод на відміну від інших рішень дозволяє запускати будь-яку програму мінімізовану, максимізовану або взагалі без вікна. Рішення llbra @ phpbrasil працює, але іноді створює небажане вікно на робочому столі, коли ви дійсно хочете, щоб завдання було запущено.
запустити 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
Я знаю, що це 100-річна посада, але все одно, я думав, що хтось може бути корисним. Ви можете помістити невидиме зображення десь на сторінці, вказуючи на URL-адресу, яка повинна працювати у фоновому режимі, наприклад:
<img src="run-in-background.php" border="0" alt="" width="1" height="1" />