2008-09-05 99 views
227

Necesito ejecutar una copia de directorio en una acción del usuario, pero los directorios son bastante grandes, por lo que me gustaría poder realizar tal acción sin que el usuario esté consciente del tiempo que lleva la copia para completar.php ejecutar un proceso en segundo plano

Cualquier sugerencia sería muy apreciada.

+3

Este http://stackoverflow.com/questions/5367261/php-exec-as-background-process-windows-wampserver-environment explica cómo hacer esto en Windows – shealtiel

Respuesta

325

Suponiendo que esta se ejecuta en una máquina Linux, siempre me he manejado de esta manera:

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

Esto inicia el comando $cmd, redirige la salida del comando a $outputfile, y escribe el identificador de proceso a $pidfile.

Esto le permite monitorear fácilmente lo que está haciendo el proceso y si aún se está ejecutando.

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; 
} 
+0

Probé tu muestra. Pero desafortunadamente nunca funcionó en este caso. $ cmd = "sudo -u sun mplayer /tmp/demo.wav>/dev/null &"; exec (sprintf ("% s>% s 2> & 1 & echo $! >>% s", $ cmd, $ archivo de salida, $ archivo pid)); echo $ pidfile; // para poder matar luego no da nada. – YumYumYum

+2

¿Sudo setup se ejecuta sin solicitar una contraseña? Cualquier comando que requiera la entrada del usuario no funcionará. –

+0

Sí, está deshabilitado, el modo sin contraseña está activado, pero todavía no funcionó. Ejemplo: #Defaults requiretty root \t ALL = (ALL) \t ALL apache \t ALL = (ALL) NOPASSWD: ALL – YumYumYum

3

¿Puede organizar un proceso por separado y luego ejecutar la copia en segundo plano? Ha pasado un tiempo desde que hice PHP, pero la función pcntl-fork parece prometedora.

+0

esto no proporciona una respuesta a la pregunta. Para criticar o solicitar aclaraciones de un autor, deje un comentario debajo de su publicación. – Rizier123

+5

Gracias - Los comentarios no existían en '08, pero lo tendré en cuenta :) –

21

Escribir el proceso como un script del lado del servidor en cualquier idioma (php/bash/Perl/etc) es útil y luego llamaremos a partir de las funciones de control de procesos en su script php.

La función probablemente detecta si io estándar se utiliza como la corriente de salida y si es entonces que se establece la devolución no value..if luego termina

Proc_Close (Proc_Open ("./command --foo=1 &", Array(), $foo)); 

He probado esto rápidamente desde la línea de comandos usando "sleep 25s" como el comando y funcionó como un amuleto.

(Answer found here)

+3

Esta es la única forma en que puedo hacer que funcione en centos. ¡Gracias! –

+0

¡Gracias! Hay una incompatibilidad de PHP y BASH. Si ejecuta un sistema más nuevo, no podrá iniciarse en segundo plano utilizando system() o exec(). Los descriptores de archivos parecen bloquearse incluso si los redirige. Su método funcionó. otra alternativa es utilizar un binario bash antiguo para PHP, pero eso es una locura – John

18

sólo me gustaría añadir un ejemplo muy simple para probar esta funcionalidad en Windows:

Cree los dos archivos siguientes y guardarlos en un directorio web:

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); 
?> 

Dar permiso IUSR escribir en el directorio en el que creó los archivos anteriores

Dar permiso IUSR a leer y ejecutar C: \ Windows \ System32 \ cmd.exe

Golpe de foreground.php un navegador web

a continuación se dictarán al navegador w/las marcas de tiempo y de recursos locales actuales # en la matriz de salida:

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

Debería ver testoutput.php en el mismo directorio donde se guardaron los archivos anteriores, y debería estar vacío

Debería ver los procesos de prueba.php en el mismo directorio que los archivos anteriores se salvaron, y debe contener el siguiente texto w/las marcas de tiempo actuales:

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

Si necesita simplemente hacer algo en el fondo sin la página PHP esperar a que se complete , podría usar otro script PHP (de fondo) que sea "invocado" con el comando wget. Este script PHP de fondo se ejecutará con privilegios, por supuesto, como cualquier otro script PHP en su sistema.

Aquí hay un ejemplo de Windows que usa wget de los paquetes gnuwin32.

El código de fondo (archivo de prueba-proc-bg.php) como un exmple ...

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

El guión primer plano, la invocación ...

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

Debe utilizar el popen/pclose para que esto funcione correctamente.

Las opciones wget:

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

Sé que es un puesto de 100 años de edad, pero de todos modos, pensaron que podría ser útil a alguien. Se puede poner una imagen invisible en algún lugar de la página apuntando a la URL que debe ejecutarse en segundo plano, como esto:

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

+11

Realmente una mala solución. Si el usuario abandona la página, el proceso se interrumpirá. –

+2

la "imagen invisible" y su enlace están expuestos en el código fuente de la página. –

1

En lugar de iniciar un proceso de fondo, ¿qué pasa con la creación de un archivo de gatillo y tener una programador como cron o autosys ejecutan periódicamente un script que busca y actúa en los archivos desencadenantes? Los desencadenantes pueden contener instrucciones o incluso comandos sin formato (mejor aún, simplemente conviértalo en un script de shell).

15

Es posible que desee intentar añadir esto a su comando

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

por ejemplo.

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

Quise decir>/dev/null 2>/dev/null & – wlf

+0

Me funciona, y usaré el makefile para hacer algo en el sitio del servidor, ¡gracias! (por ejemplo, make, make restart) –

+0

¡Esto es mucho más simple que la respuesta aceptada! (Siempre que no necesite nada más complejo, como comprobar si el proceso aún se está ejecutando). –

3

Puede probar un sistema de colas como Resque. A continuación, puede generar un trabajo, que procesa la información y regresa bastante rápido con la imagen de "procesamiento". Sin embargo, con este enfoque, no sabrás cuándo está terminado.

Esta solución está diseñada para aplicaciones de mayor escala, donde no desea que sus máquinas frontales hagan trabajos pesados, para que puedan procesar las solicitudes de los usuarios. Por lo tanto, podría funcionar o no con datos físicos como archivos y carpetas, pero para el procesamiento de lógica más complicada u otras tareas asíncronas (es decir, nuevos registros de correos) es agradable tener y muy escalable.

6

Bueno me pareció un poco más rápido y más fácil de usar la versión

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

y funciona.

+0

¿es solo Linux? –

+0

@ Alpha.Dev Lo probé solo en Linux. No sé si funcionará en otro sistema. – Morfidon

0

scripting PHP no es como el lenguaje desarrollar otra aplicación de escritorio.En los lenguajes de las aplicaciones de escritorio, podemos establecer subprocesos de daemon para ejecutar un proceso en segundo plano, pero en PHP se produce un proceso cuando el usuario solicita una página. Sin embargo, es posible establecer un trabajo en segundo plano utilizando la funcionalidad del trabajo cron del servidor, que ejecuta el script php.

1

estoy usando en gran medida fast_cgi_finish_request()

En combinación con un cierre y register_shutdown_function()

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

continuación, se registra este cierre para ser ejecutado antes de la parada.

register_shutdown_function($backgroundJob); 

Por último, cuando la respuesta se envía al cliente puede cerrar la conexión con el cliente y seguir trabajando con el proceso de PHP:

fast_cgi_finish_request(); 

El cierre se ejecutará después de fast_cgi_finish_request.

El mensaje $ no estará visible en ningún momento. Y puede registrar tantos cierres como desee, pero tenga cuidado con el tiempo de ejecución del script. Esto sólo funcionará si se está ejecutando PHP como un módulo Fast CGI (que era verdad ?!)

0

Para aquellos de nosotros usando de Windows, mira esto:

Referencia: 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. [email protected]'s solution does work but it sometimes produces an unwanted window on the desktop when you really want the task to run hidden.

inicio minimizado el Bloc de notas en el fondo:

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

iniciar un comando shell invisible en el fondo:

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

inicio Mspaint maximizada y esperar a que cierre antes de continuar la secuencia de comandos:

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

Para obtener más información sobre el método run() vaya a: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

+0

¿Qué se necesita hacer para ejecutar este código? Me sale 'Fatal error: Class 'COM' no encontrado' –

+0

P.s. El enlace a MSDN está roto –

+0

@ Alph.Dev: parece que el documento msdn se ha eliminado temporalmente. Contenía más ejemplos y explicaciones sobre cómo usar WScript.Shell. –

5

Aquí hay una función para iniciar un proceso en segundo plano en PHP. Finalmente creó uno que realmente funciona en Windows también, después de leer y probar diferentes enfoques y parámetros.

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; 
    } 
} 

Nota 1: En las ventanas, no utilice /B parámetro como se sugiere en otro lugar. Obliga al proceso a ejecutar la misma ventana de consola que el comando start, lo que hace que el proceso se procese de forma sincronizada. Para ejecutar el proceso en un hilo separado (asincrónicamente), no use /B.

Nota 2: Las comillas dobles vacías después de start "" son necesarias si el comando es una ruta entre comillas. El comando start interpreta el primer parámetro citado como título de ventana.

1

Utilice esta función para ejecutar su programa en segundo plano. Es multiplataforma y completamente personalizable.

<?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; 
} 

Tenga en cuenta que tras la orden comenzó, por defecto esta función cierra la entrada y salida estándar del proceso en ejecución. Puede redirigir la salida del proceso a algún archivo a través de los argumentos $ redirectStdout y $ redirectStderr.

Nota para usuarios de Windows: no hay forma de redireccionar stdout/stderr al dispositivo nul. Unfortunaly no puede hacerlo:

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

Por lo que debe redirigir stderr/entrada estándar al archivo válido o su comando debe contar con que hay descriptor stderr/stdout.

Notas para * usuarios nix:

1) utilizar el comando exec cáscara si desea obtener real PID:

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

2) Utilice argumento entrada estándar $ si desea pasar algunos datos a la entrada de su programa:

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

Una solución de trabajo para Windows y Linux. Encuentre más en My github page.

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; 
         } 
        } 
      } 
Cuestiones relacionadas