PHP'de eşzamansız kabuk yürütme


199

Bir kabuk komut dosyası çağırmak gerekiyor ama çıktı hakkında hiç umurumda değil bir PHP komut dosyası var. Kabuk script SOAP çağrıları bir dizi yapar ve tamamlamak için yavaş, bu yüzden bir cevap beklerken PHP isteği yavaşlatmak istemiyorum. Aslında, PHP isteği kabuk işlemini sonlandırmadan çıkabilmelidir.

Çeşitli içine baktım exec(), shell_exec(), pcntl_fork()vb fonksiyonlar, ancak bunların hiçbiri tam olarak ne istiyorum sunmak gibi görünüyor. (Ya da eğer yaparlarsa, bana nasıl olduğu açık değil.) Herhangi bir öneriniz var mı?


Hangi çözümü seçerseniz seçin, kabuk betiğinin sisteminize niceionice/usr/bin/ionice -c3 /usr/bin/nice -n19
zarar vermesini

Yanıtlar:


219

"Çıktı umurumda değil", komut dosyası exec ile çağrılamadı & işlem arka plan için ?

DÜZENLEME - @ neyi birleştiren AdamTheHut Bu yayına yorumladı, sen bir çağrı için bu ekleyebilir exec:

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

Yani hem yönlendirir stdio(ilk >) ve stderr( 2>) için/dev/null ve arka planda çalıştırın.

Aynı şeyi yapmanın başka yolları da var, ancak bu okunması en kolay yol.


Yukarıdaki çift yönlendirmeye bir alternatif:

" &> /dev/null &"

11
Bu işe yarıyor gibi görünüyor, ama bir ve işaretinden biraz daha fazlasına ihtiyacı var. Ben exec () çağrısına "> / dev / null 2> / dev / null &" ekleyerek çalıştım. Her ne kadar itiraf etmeliyim, bunun ne olduğundan tam olarak emin değilim.
AdamTheHutt

2
Kesinlikle gitmek istiyorsanız ateş ve php ve apache ile unut. Bir çok üretim Apache ve PHP ortamında pcntl_fork () devre dışı bırakılacak.
yöntem

9
Sadece bir not &> /dev/null &, xdebug bunu kullanırsanız günlük oluşturmaz. Kontrol stackoverflow.com/questions/4883171/...
kapeels

5
@MichaelJMulligan dosya tanımlayıcılarını kapatır. Bununla birlikte, verimlilik kazanımlarına rağmen, /dev/nullarka planda, FD'lere yazmak hatalara neden olurken, /dev/nullsadece sessizce hiçbir şey yapmamak için okuma veya yazma girişimleri nedeniyle, kullanım daha iyi bir uygulamadır .
Charles Duffy

3
Apache yeniden başlatıldığında arka plan komut dosyaları öldürülecek ... sadece çok uzun işler için bunun farkında olun veya şanssız zamanlama açısından ve işinizin neden kaybolduğunu merak ediyorsunuz ...
Julien

53

Kullandığım de gerçekten bağımsız bir süreç başlıyor gibi bunun için.

<?php
    `echo "the command"|at now`;
?>

4
bazı durumlarda bu kesinlikle en iyi çözümdür. bir webgui'den "sudo reboot" ("echo 'sleep 3; sudo reboot' | now") yayınlamama ve sayfayı oluşturmayı bitirmeme yarayan tek kişi oldu .. openbsd
Kaii

1
Çalıştırmak kullanıcı apache (genellikle eğer www-data) kullanımına izinleri yok atve siz bunu yapılandıramazsınız, kullanmak deneyebilirsiniz <?php exec('sudo sh -c "echo \"command\" | at now" ');varsa command, bkz tırnak içerir escapeshellarg seni kurtarmak için baş ağrısı
Julien

1
Bunu düşünmek, sudo'yu sh çalıştırmak için en iyi fikir değildir, çünkü temelde sudo'ya her şeye kök erişimi sağlar. Kullandığım döndü echo "sudo command" | at nowve yorum www-datadışarı/etc/at.deny
Julien

@Julien belki sudo komutuyla bir kabuk komut dosyası yapabilir ve onun yerine başlatabilirsiniz. kullanıcı tarafından gönderilen herhangi bir değeri söz konusu komut dosyasına geçirmediğiniz sürece
iletmediğiniz sürece

26

Tüm Windows kullanıcıları için: Eşzamansız bir PHP betiği çalıştırmak için iyi bir yol buldum (aslında hemen hemen her şeyle çalışır).

Popen () ve pclose () komutlarına dayanır. Hem Windows hem de Unix'te iyi çalışır.

function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
} 

Orijinal kod: http://php.net/manual/en/function.exec.php#86329


Bu sadece bir dosya yürütmek, php / python / node vb kullanıyorsanız çalışmıyor
david valentino

@davidvalentino Doğru, ve sorun değil! Bir PHP / Pyhton / NodeJS betiği yürütmek istiyorsanız, çalıştırılabilir dosyayı çağırmanız ve betiğinize geçirmeniz gerekir. Örn: terminalinizi myscript.jskoymazsınız, bunun yerine yazarsınız node myscript.js. Yani: düğüm çalıştırılabilir, myscript.js çalıştırılacak komut dosyasıdır. Yürütülebilir dosya ile komut dosyası arasında büyük bir fark vardır.
LucaM

Bu durumda doğru sorun değil ,, diğer durumlarda örnek laravel esnaf çalıştırmak gibi, php artisansadece yorum koymak bu yüzden neden komutları ile çalışma alışkanlık izleme gerek yok
david valentino

22

Linux'ta aşağıdakileri yapabilirsiniz:

$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);

Bu, komutu komut isteminde yürütür ve ardından çalıştığından emin olmak için> 0 için kontrol edebileceğiniz PID'yi döndürür.

Bu soru benzer: PHP'nin iş parçacığı var mı?


Sadece çıplak temel unsurları eklerseniz ( action=generate var1_id=23 var2_id=35 gen_id=535segmenti ortadan kaldırırsanız ) bu cevabı okumak daha kolay olacaktır . Ayrıca, OP bir kabuk betiği çalıştırmayı sorduğundan, PHP'ye özel bölümlere ihtiyacınız yoktur. Son kod şöyle olurdu:$cmd = 'nohup nice -n 10 /path/to/script.sh > /path/to/log/file.log & printf "%u" $!';
rinogo

Ayrıca, "daha önce orada olan" bir not olarak, bunu okuyan herkes sadece nicedeğil aynı zamanda kullanmayı düşünebilir ionice.
rinogo

1
"% U" $ ne yapar! tam olarak ne yapmalı?
MortiestMorty

@Twigs &arka planda önceki kodu çalıştırır, daha sonra PID'yi içeren değişkenin printfbiçimlendirilmiş çıktısı için kullanılır$!
NoChecksum

1
Çok teşekkür ederim, bir async PHP kabuk çağrısı ile bir günlüğe çıktı bulmak için bulduğum her türlü çözümü denedim ve tüm kriterleri yerine getiren tek.
Josh Powlison


6

Linux'ta, komutun sonuna bir ve işareti ekleyerek yeni bir bağımsız iş parçacığında bir işlem başlatabilirsiniz

mycommand -someparam somevalue &

Windows'ta "start" DOS komutunu kullanabilirsiniz

start mycommand -someparam somevalue

2
Linux'ta, alt işlem tarafından tutulan açık bir dosya tanıtıcısından (yani, stdout) okumaya çalışıyorsa, üst öğe çocuk çalışmayı bitirene kadar yine de engelleyebilir, bu nedenle bu tam bir çözüm değildir.
Charles Duffy

1
startWindows üzerinde test edilmiş komut, eşzamansız olarak çalışmıyor ... Bu bilgiyi nereden aldığınız kaynağı ekleyebilir misiniz?
Alph.Dev

@ Alph.Dev, Windows kullanıyorsanız lütfen cevabıma bir göz atın: stackoverflow.com/a/40243588/1412157
LucaM

@mynameis Yanıtınız, start komutunun neden çalışmadığını tam olarak gösteriyor. Bu /Bparametre nedeniyle . Burada açıkladım: stackoverflow.com/a/34612967/1709903
Alph.Dev

6

bunu yapmanın doğru yolu (!)

  1. çatal()
  2. setsid ()
  3. execve ()

çatal çatal, setsid geçerli işlemin ana bir (ana yok) olmasını söyler, çağrılan işlemin yerine çağrılacak işlemi söyle. böylece ebeveyn çocuğu etkilemeden istifa edebilir.

 $pid=pcntl_fork();
 if($pid==0)
 {
   posix_setsid();
   pcntl_exec($cmd,$args,$_ENV);
   // child becomes the standalone detached process
 }

 // parent's stuff
 exit();

6
Pcntl_fork () ile ilgili sorun, OP yaptığı gibi bir web sunucusu altında çalışırken kullanmamanız gerektiğidir (ayrıca, OP zaten denedim).
Guss

6

Bunu kullandım ...

/** 
 * Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.  
 * Relies on the PHP_PATH config constant.
 *
 * @param string $filename  file to execute
 * @param string $options   (optional) arguments to pass to file via the command line
 */ 
function asyncInclude($filename, $options = '') {
    exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}

( benzer veya benzer PHP_PATHbir sabit nerede tanımlanır define('PHP_PATH', '/opt/bin/php5'))

Bağımsız değişkenleri komut satırı üzerinden geçirir. PHP'de okumak için argv .


4

Benim için gerçekten işe yaradığını bulmanın tek yolu:

shell_exec('./myscript.php | at now & disown')

3
'disown' yerleşik bir Bash'tir ve shell_exec () ile bu şekilde çalışmaz. Denedim shell_exec("/usr/local/sbin/command.sh 2>&1 >/dev/null | at now & disown")ve tüm aldığım:sh: 1: disown: not found
Thomas Daugaard


2

PHP betiğini daemon veya cronjob olarak da çalıştırabilirsiniz :#!/usr/bin/php -q


1

Adlandırılmış bir fifo kullanın.

#!/bin/sh
mkfifo trigger
while true; do
    read < trigger
    long_running_task
done

Ardından, uzun süren görevi başlatmak istediğinizde, sadece bir satırsonu yazın (tetikleyici dosyasına engelleme yok).

Girişiniz küçük olduğundan PIPE_BUFve tek bir write()işlem olduğu sürece , fifo'ya argümanlar yazabilir $REPLYve komut dosyasında olduğu gibi görünmelerini sağlayabilirsiniz .


1

kullanım kuyruğu olmadan, şu şekilde kullanabilirsiniz proc_open():

    $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")    //here curaengine log all the info into stderror
    );
    $command = 'ping stackoverflow.com';
    $process = proc_open($command, $descriptorspec, $pipes);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.