PHP में एसिंक्रोनस शेल निष्पादन


199

मुझे एक PHP स्क्रिप्ट मिली है जिसे शेल स्क्रिप्ट को लागू करने की आवश्यकता है लेकिन आउटपुट के बारे में बिल्कुल भी परवाह नहीं है। शेल स्क्रिप्ट कई SOAP कॉल करता है और पूरा करने के लिए धीमा है, इसलिए मैं PHP अनुरोध को धीमा नहीं करना चाहता, जबकि यह एक उत्तर की प्रतीक्षा करता है। वास्तव में, PHP अनुरोध शेल प्रक्रिया को समाप्त किए बिना बाहर निकलने में सक्षम होना चाहिए।

मैं विभिन्न देखा है exec(), shell_exec(), pcntl_fork(), आदि काम करता है, लेकिन उनमें से कोई वास्तव में मैं क्या चाहते हैं की पेशकश करने लगते हैं। (या, अगर वे करते हैं, तो यह मेरे लिए स्पष्ट नहीं है कि कैसे।) कोई सुझाव?


कोई फर्क नहीं पड़ता जो समाधान आप चुनते हैं, आप भी उपयोग करने पर विचार करना चाहिए niceऔर ioniceअपने सिस्टम (जैसे ढेर लगने से खोल स्क्रिप्ट को रोकने के लिए /usr/bin/ionice -c3 /usr/bin/nice -n19)
rinogo

जवाबों:


218

यदि यह "आउटपुट के बारे में परवाह नहीं करता है", तो स्क्रिप्ट को निष्पादित करने &की प्रक्रिया को पृष्ठभूमि के साथ नहीं बुलाया जा सकता है?

EDIT - जिसमें @ AdamTheHut ने इस पोस्ट पर टिप्पणी की है, आप इसे कॉल में जोड़ सकते हैं exec:

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

वह stdio(पहले >) और stderr( 2>) दोनों को रीडायरेक्ट करेगा /dev/nullऔर बैकग्राउंड में चलेगा।

एक ही काम करने के अन्य तरीके हैं, लेकिन यह पढ़ने में सबसे सरल है।


उपरोक्त दोहरे पुनर्निर्देशन का एक विकल्प:

" &> /dev/null &"

11
यह काम करने लगता है, लेकिन इसे एम्परसेंड की तुलना में थोड़ा अधिक चाहिए। मैंने इसे "> / dev / null 2> / dev / null &" से निष्पादित () कॉल में जोड़कर काम किया। हालांकि मुझे मानना ​​होगा कि मुझे यकीन नहीं है कि वह क्या करता है।
एडम दिहुत

2
निश्चित रूप से अगर आप आग चाहते हैं तो जाने का रास्ता php और अपाचे के साथ भूल जाते हैं। अपाचे और PHP वातावरण के बहुत सारे उत्पादन में pcntl_fork () अक्षम होगा।
विधि

9
&> /dev/null &यदि आप इसका उपयोग करते हैं तो बस एक नोट , xdebug लॉग उत्पन्न नहीं करेगा। चेक stackoverflow.com/questions/4883171/...
kapeels

5
@MichaelJMulligan यह फाइल डिस्क्रिप्टर को बंद कर देता है। उन्होंने कहा, दक्षता लाभ के बावजूद, दृष्टिहीनता में, का उपयोग /dev/nullकरना बेहतर अभ्यास है, क्योंकि बंद एफडी को लिखने में त्रुटियां होती हैं, जबकि /dev/nullकेवल चुपचाप पढ़ने या लिखने के प्रयास कुछ भी नहीं करते हैं।
चार्ल्स डफी

3
अपाचे को रीस्टार्ट करने पर बैकग्राउंड स्क्रिप्ट मारे जाएंगे ... बस इसके बारे में बहुत लंबी नौकरियों के बारे में पता होना चाहिए या यदि आप अशुभ समय-वार हैं और आपको आश्चर्य है कि आपकी नौकरी क्यों गायब हो गई ...
जूलियन

53

मैं प्रयोग किया जाता है पर , इस के लिए यह वास्तव में एक स्वतंत्र प्रक्रिया शुरू कर रहा है के रूप में।

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

4
कुछ स्थितियों में यह पूरी तरह से सबसे अच्छा समाधान है। एक WebGUI और खत्म से |; ( "अब में 'sudo रिबूट नींद 3' गूंज") पेज प्रतिपादन .. OpenBSD पर यह केवल एक ही है कि मेरे लिए काम किया है एक "sudo रिबूट" रिलीज करने के लिए था
काइ

1
यदि आप जिस उपयोगकर्ता को अपाचे चलाते हैं (आमतौर पर www-data) के पास उपयोग करने की अनुमति नहीं है atऔर आप इसे कॉन्फ़िगर नहीं कर सकते हैं, तो आप उपयोग करने का प्रयास कर सकते हैं <?php exec('sudo sh -c "echo \"command\" | at now" ');यदि commandउद्धरण शामिल हैं, तो आपको सिरदर्द से बचाने के लिए एस्सेशेलग देखें
Julien

1
अच्छी तरह से इसके बारे में सोचने के लिए, सू को चलाने के लिए प्राप्त करना सबसे अच्छा विचार नहीं है, क्योंकि यह मूल रूप से सूडो को हर चीज की जड़ देता है। मैं उपयोग करने के लिए वापस लाया गया echo "sudo command" | at nowऔर उस पर टिप्पणी www-dataमें बाहर/etc/at.deny
जुलिएन

@ जुलिएन शायद आप सूडो कमांड के साथ एक शेल स्क्रिप्ट बना सकते हैं और इसके बजाय इसे लॉन्च कर सकते हैं। जब तक आप किसी भी उपयोगकर्ता द्वारा दिए गए मानों को स्क्रिप्ट में नहीं दे रहे हैं
Garet Claborn

26

सभी विंडोज उपयोगकर्ताओं के लिए: मुझे एक अतुल्यकालिक PHP स्क्रिप्ट चलाने का एक अच्छा तरीका मिला (वास्तव में यह लगभग हर चीज के साथ काम करता है)।

यह popen () और pclose () कमांड्स पर आधारित है। और विंडोज और यूनिक्स दोनों पर अच्छा काम करता है।

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

मूल कोड: http://php.net/manual/en/function.exec.php#86329 से


यह केवल एक फाइल को निष्पादित करता है, अगर php / python / नोड आदि का उपयोग करके काम नहीं करता है
david valentino

@davidvalentino सही है, और यह ठीक है! यदि आप एक PHP / Pyhton / NodeJS स्क्रिप्ट निष्पादित करना चाहते हैं, तो आपको वास्तव में निष्पादन योग्य को कॉल करना होगा और इसे अपनी स्क्रिप्ट में पास करना होगा। जैसे: आप अपने टर्मिनल में नहीं डालते हैं myscript.js, बल्कि आप लिखेंगे node myscript.js। वह है: नोड निष्पादन योग्य है, myscript.js निष्पादित करने के लिए अच्छी तरह से, स्क्रिप्ट है। निष्पादन योग्य और स्क्रिप्ट के बीच बहुत बड़ा अंतर है।
लुकाएम

thats सही है कि इस मामले में कोई समस्या नहीं है ,, की जरूरत की तरह अन्य मामलों उदाहरण में laravel कारीगर, चलाने के लिए php artisanसिर्फ डाल टिप्पणी यहाँ तो कोई जरूरत आदेशों के साथ कारण है कि यह अभ्यस्त काम अनुरेखण
डेविड वैलेंटिनो

22

लिनक्स पर आप निम्न कार्य कर सकते हैं:

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

यह कमांड प्रॉम्प्ट पर कमांड निष्पादित करेगा और फिर पीआईडी ​​लौटाएगा, जिसे आप यह सुनिश्चित करने के लिए> 0 के लिए जांच कर सकते हैं।

यह प्रश्न समान है: क्या PHP में थ्रेडिंग है?


इस उत्तर को पढ़ना आसान होगा यदि आपने केवल नंगे आवश्यक ( action=generate var1_id=23 var2_id=35 gen_id=535सेगमेंट को समाप्त करना ) को शामिल किया है। इसके अलावा, चूंकि ओपी ने एक शेल स्क्रिप्ट चलाने के बारे में पूछा था, इसलिए आपको PHP- विशिष्ट भागों की आवश्यकता नहीं है। अंतिम कोड होगा:$cmd = 'nohup nice -n 10 /path/to/script.sh > /path/to/log/file.log & printf "%u" $!';
rinogo

इसके अलावा, एक है जो से एक नोट के रूप में "से पहले वहाँ गया", यह पढ़ किसी को भी बस नहीं विचार कर सकते हैं nice, लेकिन यह भी ionice
rinogo

1
"% U" $ क्या करता है! ठीक है?
टिग्स

@ टाइगर्स &पृष्ठभूमि में पूर्ववर्ती कोड चलाता है, फिर printfइसका उपयोग $!चर के स्वरूपित आउटपुट के लिए किया जाता है जिसमें PID
NoChecksum

1
बहुत बहुत धन्यवाद, मैंने सभी प्रकार के समाधानों की कोशिश की, जो मुझे एक async PHP शेल कॉल के साथ एक लॉग में आउटपुट करने के लिए मिल रहा था और आपका एकमात्र ऐसा मापदंड है जो सभी मानदंडों को पूरा करता है।
जोश पावलिसन


6

लिनक्स में, आप कमांड के अंत में एम्परसेंड जोड़कर एक नए स्वतंत्र थ्रेड में एक प्रक्रिया शुरू कर सकते हैं

mycommand -someparam somevalue &

विंडोज में, आप "स्टार्ट" डॉस कमांड का उपयोग कर सकते हैं

start mycommand -someparam somevalue

2
लिनक्स पर, माता-पिता तब भी ब्लॉक कर सकते हैं जब तक कि बच्चे ने दौड़ना समाप्त नहीं कर दिया हो, यदि वह सब-डिसेस (यानी स्टडआउट) द्वारा रखे गए एक खुले फ़ाइल हैंडल से पढ़ने की कोशिश कर रहा है, तो यह एक पूर्ण समाधान नहीं है।
चार्ल्स डफी

1
startविंडोज़ पर परीक्षण किया गया कमांड, यह एसिंक्रोनस रूप से नहीं चलता है ... क्या आप उस स्रोत को शामिल कर सकते हैं जहां से आपको वह जानकारी मिली थी?
अल्फ.डेव

@ Alph.Dev कृपया मेरे उत्तर पर एक नज़र डालें यदि आप Windows का उपयोग कर रहे हैं: stackoverflow.com/a/40243588/1412157
LucaM

@mynameis आपका उत्तर वास्तव में दिखाता है कि क्यों स्टार्ट कमांड काम नहीं कर रहा था। /Bपैरामीटर की वजह से । मैंने इसे यहाँ समझाया है: stackoverflow.com/a/34612967/1709903
Alph.Dev

6

यह करने के लिए सही तरीका (!) है

  1. कांटा()
  2. setsid ()
  3. execve ()

कांटा कांटा, सेटसाइड वर्तमान प्रक्रिया को एक मास्टर (कोई माता-पिता) नहीं बनने के लिए कहता है, कॉलिंग प्रक्रिया को निष्पादित करने के लिए कहा जाता है। ताकि माता-पिता बच्चे को प्रभावित किए बिना छोड़ सकें।

 $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 () के साथ समस्या यह है कि आप वेब सर्वर के तहत चलने पर इसका उपयोग करने वाले नहीं हैं, जैसा कि ओपी करता है (इसके अलावा, ओपी ने पहले ही यह कोशिश की है)।
Guss

6

मैंने इसका इस्तेमाल किया ...

/** 
 * 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 &");
}

(जहां PHP_PATHएक कांस्टेबल की तरह परिभाषित define('PHP_PATH', '/opt/bin/php5')या समान है)

यह कमांड लाइन के माध्यम से तर्कों में गुजरता है। उन्हें PHP में पढ़ने के लिए, argv देखें ।


4

एकमात्र तरीका जो मैंने पाया कि वास्तव में मेरे लिए काम किया गया था:

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

3
'डिसऑन' एक बैश बिल्ट-इन है और शेल_एक्सईसी () के साथ काम नहीं करता है। मैंने कोशिश की shell_exec("/usr/local/sbin/command.sh 2>&1 >/dev/null | at now & disown")और मुझे सब मिल गया:sh: 1: disown: not found
थॉमस दौगार्ड १५'१३

4

मुझे इसके लिए सिम्फनी प्रोसेस कंपोनेंट भी उपयोगी लगा।

use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
// ... run process in background
$process->start();

// ... do other things

// ... if you need to wait
$process->wait();

// ... do things after the process has finished

देखें कि यह अपने GitHub रेपो में कैसे काम करता है ।


2
चेतावनी: यदि आप प्रतीक्षा नहीं करते हैं, तो अनुरोध समाप्त होने पर प्रक्रिया को मार दिया जाएगा
the_nuts

सही उपकरण, जो proc_*आंतरिक कार्यों पर आधारित है ।
मचिट्ठार्गा


1

एक नामित पण का उपयोग करें।

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

फिर जब भी आप लंबे समय तक चलने वाले कार्य को शुरू करना चाहते हैं, तो बस एक नई पंक्ति लिखें (ट्रिगर फ़ाइल के लिए नॉनब्लॉकिंग)।

जब तक आपका इनपुट इससे छोटा है PIPE_BUFऔर यह एक एकल write()ऑपरेशन है, तब तक आप पोजो में तर्क लिख सकते हैं और उन्हें $REPLYस्क्रिप्ट में दिखा सकते हैं ।


1

उपयोग कतार के बिना, आप 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);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.