PHP의 비동기 셸 실행


199

쉘 스크립트를 호출해야하지만 출력에 전혀 신경 쓰지 않는 PHP 스크립트가 있습니다. 셸 스크립트는 많은 SOAP 호출을 수행하며 완료 속도가 느리므로 응답을 기다리는 동안 PHP 요청 속도를 늦추고 싶지 않습니다. 실제로 PHP 요청은 쉘 프로세스를 종료하지 않고 종료 할 수 있어야합니다.

나는 여러 가지로 검토 한 결과 exec(), shell_exec(), pcntl_fork(), 등의 기능을하지만, 그들 중 누구도 정확히 내가 원하는 것을 제공하기 위해 보이지 않는다. (또는 그들이 그렇게한다면 방법이 명확하지 않습니다.) 어떤 제안?


당신이 선택하는 솔루션에 상관없이, 당신은 또한 사용을 고려해야 nice하고 ionice(예를 들어, 시스템을 마비시키는 쉘 스크립트를 방지하기 위해 /usr/bin/ionice -c3 /usr/bin/nice -n19)
rinogo

답변:


218

"출력에 신경 쓰지 않으면" &프로세스의 백그라운드로 스크립트에 대한 exec를 호출 할 수 없습니까?

편집 -@ AdamTheHut 이이 게시물에 댓글을 달았을 때이를 호출에 추가 할 수 있습니다 exec.

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

그러면 stdio(first >) 및 stderr( 2>)가 /dev/null백그라운드로 리디렉션 되어 백그라운드에서 실행됩니다.

같은 일을하는 다른 방법이 있지만 가장 읽기 쉬운 방법입니다.


위의 이중 리디렉션에 대한 대안 :

" &> /dev/null &"

11
이것은 작동하는 것처럼 보이지만 앰퍼샌드보다 조금 더 필요합니다. exec () 호출에 "> / dev / null 2> / dev / null &"를 추가하여 작동시킵니다. 인정해야하지만 그것이 정확히 무엇인지 잘 모르겠습니다.
AdamTheHutt

2
당신이 불을 원하고 PHP와 아파치를 잊어 버릴 경우 확실히 갈 길. 많은 프로덕션 Apache 및 PHP 환경에서 pcntl_fork ()가 비활성화됩니다.
방법

9
단지에 대한 메모 &> /dev/null &당신이 사용하는 경우 Xdebug는이 로그를 생성하지 않습니다. 확인 stackoverflow.com/questions/4883171/...은
kapeels

5
@MichaelJMulligan 파일 설명자를 닫습니다. 그러나 효율성 향상에도 불구하고 /dev/null닫힌 FD에 쓰기 작업을 수행하면 오류가 발생 /dev/null하지 않고 단순히 자동으로 아무 작업도 수행하지 않기 때문에 사용 하는 것이 더 나은 방법 입니다.
Charles Duffy

3
배경 스크립트는 아파치를 다시 시작할 때 ... 단지 매우 긴 작업이 인식 또는 살해 될 것입니다 당신은 타이밍이 많다는 재수하고 작업이 사라진 이유가 궁금하면 ...
줄리앙

53

실제로 독립 프로세스를 시작하기 때문에 이것을 사용 했습니다 .

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

4
어떤 상황에서는 이것이 절대적으로 최상의 솔루션입니다. 오픈 BSD에 .. 페이지를 렌더링 웹 GUI와 마무리에서 | ( "지금에서 'sudo를 재부팅 잠 3'에코을") 그것은 "sudo를 재부팅"을 출시 나를 위해 일한 유일한 사람이었다
Kaii

1
아파치를 실행하는 사용자 (일반적으로 www-data)가 사용할 권한이 at없고이를 구성 할 수없는 <?php exec('sudo sh -c "echo \"command\" | at now" ');경우 command, 따옴표가 포함되어 있으면 escapeshellarg 를 참조 하여 두통을 피하십시오
Julien

1
sudo를 실행하여 sh를 실행하는 것은 기본적으로 sudo가 모든 것에 대한 루트 액세스 권한을 부여하기 때문에 최선의 아이디어는 아닙니다. 내가 사용하는 복귀 echo "sudo command" | at now및 주석 www-data에서/etc/at.deny
줄리앙

@Julien은 sudo 명령으로 쉘 스크립트를 작성하고 대신 실행할 수 있습니다. 해당 스크립트에 사용자가 제출 한 값을 전달하지 않는 한
Garet Claborn

26

모든 Windows 사용자에게 : 비동기 PHP 스크립트를 실행하는 좋은 방법을 찾았습니다 (실제로 거의 모든 것이 작동합니다).

popen () 및 pclose () 명령을 기반으로합니다. Windows와 Unix에서 모두 잘 작동합니다.

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 / node 등을 사용하는 경우 작동하지 않습니다
david valentino

@ davidvalentino 맞습니다. 괜찮습니다! PHP / Pyhton / NodeJS 스크립트를 실행하려면 실제로 실행 파일을 호출하고 스크립트에 전달해야합니다. 예 : 터미널에 넣지 myscript.js않고 대신 씁니다 node myscript.js. 즉 : node 는 실행 파일이고 myscript.js 는 실행할 스크립트입니다. 실행 파일과 스크립트에는 큰 차이가 있습니다.
LucaM

필요 같은 다른 경우의 예에서 바로 그게이 경우에 아무 문제가 ,, laravel의 장인, 실행 php artisan여기에 바로 넣어 코멘트를 너무 명령을 왜 못해 작업을 추적 할 필요 없다
데이비드 발렌티노

22

Linux에서는 다음을 수행 할 수 있습니다.

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

명령 프롬프트에서 명령을 실행 한 다음 PID를 반환하면> 0을 확인하여 작동하는지 확인할 수 있습니다.

이 질문은 비슷합니다 : PHP에는 스레딩이 있습니까?


필수 요소 만 포함하면 ( action=generate var1_id=23 var2_id=35 gen_id=535세그먼트 제거 ) 이 답변을보다 쉽게 ​​읽을 수 있습니다 . 또한 OP가 쉘 스크립트 실행에 대해 질문 했으므로 PHP 관련 부분이 필요하지 않습니다. 최종 코드는 다음과 같습니다.$cmd = 'nohup nice -n 10 /path/to/script.sh > /path/to/log/file.log & printf "%u" $!';
rinogo

또한, "이전에 갔다"는 사람의 메모로서, 이것을 읽는 사람은 누구나 nice뿐만 아니라을 사용하는 것을 고려할 수 있습니다 ionice.
rinogo

1
"% u"$는 무엇입니까! 정확히합니까?
Twigs

@Twigs &는 백그라운드에서 선행 코드를 실행 한 다음 PID를 포함하는 변수 printf의 형식화 된 출력에 사용됩니다.$!
NoChecksum

1
대단히 감사합니다. 비동기 PHP 셸 호출로 로그에 출력하기 위해 찾은 모든 종류의 솔루션을 시도했으며 귀하의 모든 기준을 충족시키는 유일한 솔루션입니다.
Josh Powlison


6

Linux에서는 명령 끝에 앰퍼샌드를 추가하여 새로운 독립 스레드에서 프로세스를 시작할 수 있습니다.

mycommand -someparam somevalue &

Windows에서는 "start"DOS 명령을 사용할 수 있습니다

start mycommand -someparam somevalue

2
Linux에서는 하위 프로세스가 보유한 열린 파일 핸들 (예 : stdout)에서 읽으려고하는 경우 하위가 실행을 완료 할 때까지 상위를 여전히 차단할 수 있으므로 완전한 솔루션은 아닙니다.
Charles Duffy

1
startWindows에서 테스트 된 명령으로 비동기식으로 실행되지 않습니다 ... 해당 정보를 얻은 소스를 포함시킬 수 있습니까?
Alph.Dev

@ Alph.Dev Windows를 사용하는 경우 내 대답을 살펴보십시오. stackoverflow.com/a/40243588/1412157
LucaM

@mynameis 답은 start 명령이 작동하지 않은 이유를 정확하게 보여줍니다. /B매개 변수 때문입니다 . 여기에 설명했습니다 : stackoverflow.com/a/34612967/1709903
Alph.Dev

6

그것을하는 올바른 방법 (!)

  1. 포크()
  2. setsid ()
  3. execve ()

포크 포크, setsid는 현재 프로세스가 마스터 프로세스 (부모 없음)가되고 호출 프로세스가 호출 된 프로세스로 대체되도록 지시합니다. 부모는 자녀에게 영향을 미치지 않고 종료 할 수 있습니다.

 $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 ()의 ​​문제점은 웹 서버에서 실행될 때 OP 가하는 것처럼 (OP는 이미 시도했지만) 사용하지 않아야한다는 것입니다.
거스

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
'disown'은 Bash 내장이며 shell_exec ()와는 작동하지 않습니다. 나는 시도 shell_exec("/usr/local/sbin/command.sh 2>&1 >/dev/null | at now & disown")하고 내가 얻는 것은 :sh: 1: disown: not found
Thomas Daugaard

4

또한 Symfony Process Component가 유용하다는 것을 알았습니다 .

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_*내부 기능을 기반으로 한 완벽한 도구 입니다.
MAChitgarha 2019 년

2

PHP 스크립트를 daemon 또는 cronjob 으로 실행할 수도 있습니다 .#!/usr/bin/php -q


1

명명 된 fifo를 사용하십시오.

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

그런 다음 장기 실행 작업을 시작할 때마다 개행을 작성하십시오 (트리거 파일에 대한 비 차단).

입력이 작고 PIPE_BUF단일 write()작업 인 경우에는 fifo에 인수를 작성 $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);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.