Existe-t-il un moyen d'utiliser shell_exec sans attendre la fin de la commande?

95

J'ai une tâche intensive de processus que je voudrais exécuter en arrière-plan.

L'utilisateur clique sur une page, le script PHP s'exécute, et enfin, en fonction de certaines conditions, si nécessaire, il doit exécuter un script shell, EG:

shell_exec('php measurePerformance.php 47 844 [email protected]');

Actuellement, j'utilise shell_exec , mais cela nécessite que le script attende une sortie. Existe-t-il un moyen d'exécuter la commande que je souhaite sans attendre qu'elle se termine?

ToughPal
la source
REMARQUE: Le processus doit démarrer immédiatement, donc un cron n'est pas une option.
ToughPal
2
Juste un petit mot: assurez-vous que la page ne sera pas accessible à tous (moteurs de recherche / utilisateurs frauduleux) et si vous êtes sur un environnement partagé, le temps d'exécution de tous vos scripts (notamment php!) Est probablement limité. Dans tous les cas, vous pouvez jeter un œil à set_time_limit / php.ini.
merkuro
Possible duplication de php exécuter un processus d'arrière-plan
Sean the Bean

Réponses:

150

Que diriez-vous d'ajouter.

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

shell_exec('php measurePerformance.php 47 844 [email protected] > /dev/null 2>/dev/null &');

Notez que cela supprime également stdio et stderr.

trembler
la source
2
Non, aucun effet secondaire. Si vous décidez parfois que vous voulez la sortie stdio ou stderr de votre processus, pensez à stackoverflow.com/questions/45953/…
jitter
1
Question: Cela ne fait que supprimer la sortie, non? PHP attend-il toujours que le processus se termine avant de continuer l'exécution, ou est-il déclenché et oublié?
Alan Storm
5
Oui, élimine. Yes fire and forget
jitter
3
Remarque: > /dev/null 2>&1 &comme on le voit dans d'autres réponses est une forme laconique de > /dev/null 2>/dev/null &donnée ici. Cela signifie essentiellement "rediriger STDERR (2) au même endroit que STDOUT (& 1)".
rymo
3
J'obtiens généralement le PID du script en cours en ajoutant echo $! >> /tmp/pid.txt à la fin de la commande exec mais en redirigeant la sortie et l'erreur vers / dev / null, plus de fichier pid: l'un de vous peut-il obtenir le PID du script lancé par exec sans attendre la sortie?
hugsbrugs
28

Cela exécutera une commande et se déconnectera du processus en cours. Bien sûr, cela peut être n'importe quelle commande que vous souhaitez. Mais pour un test, vous pouvez créer un fichier php avec une commande sleep (20) it.

exec("nohup /usr/bin/php -f sleep.php > /dev/null 2>&1 &");
Brent Baisley
la source
est-ce mieux ceci ou shell_exec?
realtebo
salut, puis-je exécuter la fonction en utilisant ce shell_exec ("nohup / usr / bin / php -f example.com/notifications/sendnotifications_async > / dev / null 2> & 1 &");
Waseem Bashir
Pourquoi les deux nohup et le &?
bugmenot123
11

Vous pouvez également rendre votre sortie au client instantanément et continuer à traiter votre code PHP par la suite.

C'est la méthode que j'utilise pour les appels Ajax qui attendent depuis longtemps et qui n'auraient aucun effet côté client:

ob_end_clean();
ignore_user_abort();
ob_start();
header("Connection: close");
echo json_encode($out);
header("Content-Length: " . ob_get_length());
ob_end_flush();
flush();
// execute your command here. client will not wait for response, it already has one above.

Vous pouvez trouver l'explication détaillée ici: http://oytun.co/response-now-process-later

Oytun
la source
1
Ops ... J'étais en train de réveiller une question de zombie ici, je n'ai pas réalisé. Mais cette information pourrait être bénéfique pour les autres.
Oytun
3
Cela n'a rien à voir avec shell_exec
bugmenot123
8

Sous Windows 2003, pour appeler un autre script sans attendre, j'ai utilisé ceci:

$commandString = "start /b c:\\php\\php.EXE C:\\Inetpub\\wwwroot\\mysite.com\\phpforktest.php --passmsg=$testmsg"; 
pclose(popen($commandString, 'r'));

Cela ne fonctionne qu'APRÈS avoir donné des autorisations modifiées sur cmd.exe- ajoutez Lecture et Exécution pour IUSR_YOURMACHINE(j'ai également défini l'écriture sur Refuser).

Jason Plank
la source
7

Bien sûr, car windowsvous pouvez utiliser:

$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("C:/path/to/php-win.exe -f C:/path/to/script.php", 0, false);

Remarque:

Si vous obtenez une COMerreur, ajoutez l'extension à votre php.iniet redémarrez apache:

[COM_DOT_NET]
extension=php_com_dotnet.dll
CONvid19
la source
Je n'ai aucune idée mais cela a fonctionné pour moi. S'il vous plaît, n'importe qui explique ce qu'est wscript.shell et ce qu'il fait.
GeekWithGlasses
pouvez-vous expliquer pourquoi cette ligne ne donne aucune sortie? `$ oExec = $ WshShell-> Exécuter ('C: \ Users \ Shreyash \ Desktop \ phantomjs-2.1.1-windows \ bin \ phantomjs.exe -f C: \ xampp \ htdocs \ composer \ test_0.js', 0 , false); `
GeekWithGlasses
Vous pouvez avoir une erreur sur test_0.js, vérifiez le journal fantôme.
CONvid19
6

Utilisez la popencommande PHP , par exemple:

pclose(popen("start c:\wamp\bin\php.exe c:\wamp\www\script.php","r"));

Cela créera un processus enfant et le script s'exécutera en arrière-plan sans attendre la sortie.

Jason Plank
la source
4
Il attend la fin du processus enfant. Au moins sur Win
Max Chernopolsky
L'arrière-plan ici est réalisé à l'aide de la commande Windows "start" qui préfixe l'exe que vous voulez exécuter, cela ne fonctionnera pas si vous l'omettez, et je ne m'attendrais pas à ce que cela fonctionne sur un autre système d'exploitation ... mais cela fonctionne fonctionne pour moi, merci! :-)
Antony
1

Si c'est en dehors d'une page Web, je recommande de générer un signal quelconque (déposer un fichier dans un répertoire, peut-être) et d'avoir un travail cron ramasser le travail qui doit être fait. Sinon, nous allons probablement entrer dans le domaine de l'utilisation pcntl_fork()et exec()de l'intérieur d'un processus Apache, et c'est juste un mauvais mojo.

le chaos
la source
1
vous avez dit que c'est un mauvais mojo. pouvez-vous s'il vous plaît élaborer?
Mayank
1

Cela fonctionnera mais vous devrez faire attention à ne pas surcharger votre serveur car il créera un nouveau processus à chaque fois que vous appelez cette fonction qui s'exécutera en arrière-plan. Si un seul appel simultané en même temps, cette solution de contournement fera le travail.

Sinon, je vous conseillerais d'exécuter une file d'attente de messages comme par exemple beanstalkd / gearman / amazon sqs.

Alfred
la source