Muszę wykonać kopię katalogu po akcji użytkownika, ale katalogi są dość duże, więc chciałbym móc wykonać taką akcję bez uświadomienia użytkownikowi czasu potrzebnego na wykonanie kopii.
Wszelkie sugestie będą mile widziane.
Muszę wykonać kopię katalogu po akcji użytkownika, ale katalogi są dość duże, więc chciałbym móc wykonać taką akcję bez uświadomienia użytkownikowi czasu potrzebnego na wykonanie kopii.
Wszelkie sugestie będą mile widziane.
Odpowiedzi:
Zakładając, że działa na komputerze z systemem Linux, zawsze postępowałem w ten sposób:
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));
Uruchamia to polecenie $cmd
, przekierowuje dane wyjściowe polecenia $outputfile
i zapisuje identyfikator procesu $pidfile
.
To pozwala łatwo monitorować, co robi proces i czy nadal działa.
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;
}
Napisz proces jako skrypt po stronie serwera w dowolnym języku (php / bash / perl / etc), a następnie wywołaj go z funkcji kontroli procesu w skrypcie php.
Funkcja prawdopodobnie wykrywa, czy jako strumień wyjściowy zastosowano standardowe io, a jeśli tak, to ustawi wartość zwracaną .. jeśli nie, to kończy się
proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );
Przetestowałem to szybko z wiersza poleceń, używając polecenia „sleep 25s” jako polecenia i działało to jak urok.
Możesz spróbować dołączyć to do swojego polecenia
>/dev/null 2>/dev/null &
na przykład.
shell_exec('service named reload >/dev/null 2>/dev/null &');
Chciałbym tylko dodać bardzo prosty przykład testowania tej funkcji w systemie Windows:
Utwórz następujące dwa pliki i zapisz je w katalogu internetowym:
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);
?>
Zezwól IUSR na zapis w katalogu, w którym utworzono powyższe pliki
Zezwól IUSR na CZYTANIE I WYKONYWANIE C: \ Windows \ System32 \ cmd.exe
Hit foreground.php z przeglądarki internetowej
Poniższe powinny zostać wyświetlone w przeglądarce z aktualnymi znacznikami czasu i zasobem lokalnym # w tablicy wyjściowej:
loading page
calling run_background_process
foreground start time = 1266003600
foreground end time = 1266003600
output = Array
(
[0] => 15010
)
end of page
Powinieneś zobaczyć testoutput.php w tym samym katalogu, w którym zostały zapisane powyższe pliki, i powinien być pusty
Powinieneś zobaczyć testprocesses.php w tym samym katalogu, w którym zostały zapisane powyższe pliki, i powinien zawierać następujący tekst z aktualnymi znacznikami czasu:
foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610
Jeśli potrzebujesz zrobić coś w tle bez oczekiwania strony PHP na zakończenie, możesz użyć innego (w tle) skryptu PHP, który jest „wywoływany” poleceniem wget. Ten skrypt PHP w tle będzie wykonywany z uprawnieniami, oczywiście, jak każdy inny skrypt PHP w twoim systemie.
Oto przykład systemu Windows używającego wget z pakietów gnuwin32.
Kod w tle (plik test-proc-bg.php) jako przykład ...
sleep(5); // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file
Skrypt na pierwszym planie, ten wywołujący ...
$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);
Musisz użyć popen / pclose, aby to działało poprawnie.
Opcje wget:
-q keeps wget quiet.
-O - outputs to stdout.
-b works on background
Oto funkcja umożliwiająca uruchomienie procesu w tle w PHP. W końcu stworzyłem taki, który faktycznie działa również w systemie Windows, po wielu odczytach i testach różnych podejść i parametrów.
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;
}
}
Uwaga 1: W systemie Windows nie używaj /B
parametru jak sugerowano gdzie indziej. Zmusza proces do uruchomienia tego samego okna konsoli co start
samo polecenie, co powoduje, że proces jest przetwarzany synchronicznie. Aby uruchomić proces w osobnym wątku (asynchronicznie), nie używaj /B
.
Uwaga 2: Puste podwójne cudzysłowy start ""
są wymagane, jeśli polecenie jest ścieżką cytowaną. start
polecenie interpretuje pierwszy cytowany parametr jako tytuł okna.
Czy potrafisz zorganizować oddzielny proces, a następnie uruchomić kopię w tle? Minęło trochę czasu odkąd zrobiłem PHP, ale funkcja pcntl-fork wygląda obiecująco.
Użyj tej funkcji, aby uruchomić program w tle. Jest wieloplatformowy iw pełni konfigurowalny.
<?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;
}
Zauważ, że po uruchomieniu polecenia ta funkcja domyślnie zamyka standardowe wejście i standardowe wyjście uruchomionego procesu. Możesz przekierować dane wyjściowe do jakiegoś pliku za pomocą argumentów $ redirectStdout i $ redirectStderr.
Uwaga dla użytkowników systemu Windows:
Nie można przekierować stdout / stderr nul
w następujący sposób:
startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');
Możesz to jednak zrobić:
startBackgroundProcess('ping yandex.com >nul 2>&1');
Uwagi dla użytkowników * nix:
1) Użyj polecenia powłoki exec, jeśli chcesz uzyskać rzeczywisty PID:
$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));
2) Użyj argumentu $ stdin, jeśli chcesz przekazać niektóre dane do danych wejściowych programu:
startBackgroundProcess('cat > input.txt', "Hello world!\n");
Możesz wypróbować system kolejkowania, taki jak Resque . Następnie możesz wygenerować zadanie, które przetwarza informacje i dość szybko powraca z obrazem „przetwarzającym”. Dzięki takiemu podejściu nie będziesz wiedział, kiedy zostanie zakończony.
To rozwiązanie jest przeznaczone do zastosowań na większą skalę, gdzie nie chcesz, aby twoje maszyny czołowe wykonywały ciężkie podnoszenie, aby mogły przetwarzać żądania użytkowników. Dlatego może, ale nie musi, pracować z danymi fizycznymi, takimi jak pliki i foldery, ale do przetwarzania bardziej skomplikowanej logiki lub innych zadań asynchronicznych (tj. Nowych wiadomości rejestracyjnych) dobrze jest mieć i jest bardzo skalowalny.
Działające rozwiązanie zarówno dla systemu Windows, jak i Linux. Znajdź więcej na stronie Mój 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;
}
}
}
Dzięki tej odpowiedzi : doskonałym narzędziem do uruchomienia procesu w tle byłby Symfony Process Component , który jest oparty na proc_*
funkcjach, ale jest o wiele łatwiejszy w użyciu. Więcej informacji znajduje się w jego dokumentacji .
Zamiast inicjować proces w tle, co z tworzeniem pliku wyzwalającego i planowaniem, takim jak cron lub autosys, co jakiś czas wykonuje skrypt, który wyszukuje i działa na pliki wyzwalające? Wyzwalacze mogą zawierać instrukcje lub nawet surowe polecenia (jeszcze lepiej, po prostu uczyń go skryptem powłoki).
Jeśli używasz PHP, możesz to zrobić o wiele łatwiej, używając pcntl_fork:
Mocno używam fast_cgi_finish_request()
W połączeniu z zamknięciem i register_shutdown_function ()
$message ='job executed';
$backgroundJob = function() use ($message) {
//do some work here
echo $message;
}
Następnie zarejestruj to zamknięcie, które ma zostać wykonane przed wyłączeniem.
register_shutdown_function($backgroundJob);
Wreszcie, gdy odpowiedź została wysłana do klienta, możesz zamknąć połączenie z klientem i kontynuować pracę z procesem PHP:
fast_cgi_finish_request();
Zamknięcie zostanie wykonane po fast_cgi_finish_request.
Wiadomość $ nie będzie widoczna w żadnym momencie. Możesz zarejestrować tyle zamknięć, ile chcesz, ale zadbaj o czas wykonania skryptu. Działa to tylko wtedy, gdy PHP działa jako moduł Fast CGI (czy to prawda ?!)
Skrypty PHP nie są podobne do innych aplikacji rozwijających język. W językach aplikacji komputerowych możemy ustawić wątki demonów, aby uruchamiały proces w tle, ale w PHP proces występuje, gdy użytkownik żąda strony. Możliwe jest jednak ustawienie zadania w tle za pomocą funkcji zadania cron serwera, którą uruchamia skrypt php.
Dla tych z nas korzystających z systemu Windows spójrz na to:
Odniesienie: http://php.net/manual/en/function.exec.php#43917
Ja też zmagałem się z uruchomieniem programu w tle w systemie Windows, podczas gdy skrypt nadal działa. Ta metoda, w przeciwieństwie do innych rozwiązań, pozwala uruchomić dowolny program zminimalizowany, zmaksymalizowany lub bez okna. Rozwiązanie llbra @ phpbrasil działa, ale czasami tworzy niechciane okno na pulpicie, gdy naprawdę chcesz, aby zadanie działało ukryte.
uruchom zminimalizowany Notepad.exe w tle:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe", 7, false);
?>
uruchom polecenie powłoki niewidoczne w tle:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
?>
uruchom MSPaint zmaksymalizowany i poczekaj na zamknięcie go przed kontynuowaniem skryptu:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("mspaint.exe", 3, true);
?>
Więcej informacji na temat metody Run () znajduje się na stronie: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp
Edytowany adres URL:
Przejdź do https://technet.microsoft.com/en-us/library/ee156605.aspx zamiast tego, ponieważ powyższy link już nie istnieje.
Fatal error: Class 'COM' not found
Wiem, że to post ma 100 lat, ale w każdym razie pomyślałem, że może się komuś przydać. Możesz umieścić niewidoczny obraz gdzieś na stronie wskazujący adres URL, który musi być uruchomiony w tle, w następujący sposób:
<img src="run-in-background.php" border="0" alt="" width="1" height="1" />