ฉันต้องดำเนินการคัดลอกไดเรกทอรีตามการกระทำของผู้ใช้ แต่ไดเรกทอรีมีขนาดค่อนข้างใหญ่ดังนั้นฉันจึงอยากจะสามารถดำเนินการดังกล่าวได้โดยที่ผู้ใช้ไม่ได้ตระหนักถึงเวลาที่ใช้ในการทำสำเนาให้เสร็จ
ข้อเสนอแนะใด ๆ ที่จะได้รับการชื่นชมมาก
ฉันต้องดำเนินการคัดลอกไดเรกทอรีตามการกระทำของผู้ใช้ แต่ไดเรกทอรีมีขนาดค่อนข้างใหญ่ดังนั้นฉันจึงอยากจะสามารถดำเนินการดังกล่าวได้โดยที่ผู้ใช้ไม่ได้ตระหนักถึงเวลาที่ใช้ในการทำสำเนาให้เสร็จ
ข้อเสนอแนะใด ๆ ที่จะได้รับการชื่นชมมาก
คำตอบ:
สมมติว่านี่ทำงานบนเครื่องลีนุกซ์ฉันก็มักจะจัดการแบบนี้เสมอ:
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));
การเปิดตัวนี้คำสั่ง$cmd
เปลี่ยนเส้นทางออกคำสั่งไป$outputfile
และเขียนกระบวนการ id $pidfile
ไป
ซึ่งช่วยให้คุณตรวจสอบกระบวนการที่กำลังทำอยู่ได้อย่างง่ายดายและถ้ามันยังทำงานอยู่
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;
}
เขียนกระบวนการเป็นสคริปต์ฝั่งเซิร์ฟเวอร์ในภาษาใดก็ได้ (php / bash / perl / etc) นั้นมีประโยชน์และเรียกมันจากฟังก์ชั่นการควบคุมกระบวนการในสคริปต์ php ของคุณ
ฟังก์ชั่นอาจตรวจพบว่ามีการใช้มาตรฐาน io เป็นกระแสข้อมูลขาออกหรือไม่และหากเป็นเช่นนั้นจะเป็นการตั้งค่าส่งคืนถ้าไม่สิ้นสุด
proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );
ฉันทดสอบสิ่งนี้อย่างรวดเร็วจากบรรทัดคำสั่งโดยใช้ "sleep 25s" เป็นคำสั่งและทำงานเหมือนเครื่องราง
(หาคำตอบได้ที่นี่ )
คุณอาจต้องการลองผนวกสิ่งนี้ลงในคำสั่งของคุณ
>/dev/null 2>/dev/null &
เช่น.
shell_exec('service named reload >/dev/null 2>/dev/null &');
ฉันต้องการเพิ่มตัวอย่างง่ายๆสำหรับการทดสอบฟังก์ชันการทำงานนี้บน Windows:
สร้างสองไฟล์ต่อไปนี้และบันทึกลงในสารบบเว็บ:
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);
?>
ให้สิทธิ์ IUSR ในการเขียนไปยังไดเรกทอรีที่คุณสร้างไฟล์ด้านบน
ให้สิทธิ์ IUSR ในการอ่านและดำเนินการ C: \ Windows \ System32 \ cmd.exe
กดปุ่ม foreground.php จากเว็บเบราว์เซอร์
ควรแสดงผลต่อไปนี้ไปยังเบราว์เซอร์ที่มีการประทับเวลาปัจจุบันและทรัพยากรท้องถิ่น # ในอาร์เรย์ผลลัพธ์:
loading page
calling run_background_process
foreground start time = 1266003600
foreground end time = 1266003600
output = Array
(
[0] => 15010
)
end of page
คุณควรเห็น testoutput.php ในไดเรกทอรีเดียวกันกับไฟล์ที่บันทึกไว้ข้างต้นและควรจะว่างเปล่า
คุณควรเห็น testprocesses.php ในไดเรกทอรีเดียวกันกับไฟล์ข้างต้นได้รับการบันทึกและควรมีข้อความต่อไปนี้ที่มีเวลาปัจจุบัน
foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610
หากคุณต้องการทำอะไรบางอย่างในพื้นหลังโดยไม่ต้องมีหน้า PHP รอจนเสร็จคุณสามารถใช้สคริปต์ PHP (พื้นหลัง) อื่นที่ "เรียกใช้" ด้วยคำสั่ง wget พื้นหลังสคริปต์ PHP นี้จะถูกดำเนินการด้วยสิทธิ์แน่นอนเป็นสคริปต์ PHP อื่น ๆ ในระบบของคุณ
นี่คือตัวอย่างบน Windows โดยใช้ wget จากแพ็คเกจ gnuwin32
รหัสพื้นหลัง (ไฟล์ test-proc-bg.php) เป็น exmple ...
sleep(5); // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file
สคริปต์เบื้องหน้าสคริปต์ที่เรียกใช้ ...
$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);
คุณต้องใช้ popen / pclose เพื่อให้มันทำงานได้อย่างถูกต้อง
ตัวเลือก wget:
-q keeps wget quiet.
-O - outputs to stdout.
-b works on background
นี่คือฟังก์ชั่นเพื่อเปิดตัวกระบวนการพื้นหลังใน PHP ในที่สุดก็สร้างหนึ่งที่ใช้งานได้จริงบน Windows เช่นกันหลังจากการอ่านและการทดสอบแนวทางและพารามิเตอร์ต่าง ๆ มากมาย
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;
}
}
หมายเหตุ 1: บน windows ห้ามใช้/B
พารามิเตอร์ตามที่แนะนำที่อื่น มันบังคับให้กระบวนการทำงานหน้าต่างคอนโซลเดียวกับstart
คำสั่งตัวเองส่งผลให้กระบวนการที่กำลังประมวลผลพร้อมกัน กระบวนการทำงานในหัวข้อที่แยกต่างหาก (ถ่ายทอดสด) /B
ไม่ได้ใช้
หมายเหตุ 2: start ""
จำเป็นต้องใช้เครื่องหมายคำพูดคู่ที่ว่างเปล่าหลังจากนั้นหากคำสั่งนั้นเป็นพา ธ ที่ยกมา start
คำสั่งตีความพารามิเตอร์ที่ยกมาครั้งแรกเป็นชื่อหน้าต่าง
ฉันพบรุ่นที่เร็วขึ้นและใช้ง่ายขึ้นเล็กน้อย
shell_exec('screen -dmS $name_of_screen $command');
และมันใช้งานได้
คุณสามารถจัดเรียงเพื่อแยกกระบวนการที่แยกต่างหากจากนั้นเรียกใช้สำเนาของคุณในพื้นหลังได้หรือไม่? เป็นเวลานานแล้วที่ฉันทำ PHP แต่ฟังก์ชั่นpcntl-fork นั้นดูดี
ใช้ฟังก์ชันนี้เพื่อเรียกใช้โปรแกรมของคุณเป็นพื้นหลัง มันข้ามแพลตฟอร์มและปรับแต่งได้อย่างเต็มที่
<?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;
}
โปรดทราบว่าหลังจากคำสั่งเริ่มต้นโดยค่าเริ่มต้นฟังก์ชั่นนี้จะปิด stdin และ stdout ของกระบวนการทำงาน คุณสามารถเปลี่ยนเส้นทางผลลัพธ์กระบวนการไปยังไฟล์บางไฟล์ผ่านทาง $ redirectStdout และ $ redirectStderr
หมายเหตุสำหรับผู้ใช้ windows:
คุณไม่สามารถเปลี่ยนเส้นทาง stdout / stderr ไปnul
ในลักษณะต่อไปนี้:
startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');
อย่างไรก็ตามคุณสามารถทำได้:
startBackgroundProcess('ping yandex.com >nul 2>&1');
หมายเหตุสำหรับผู้ใช้ * nix:
1) ใช้คำสั่ง exec shell หากคุณต้องการรับ PID จริง:
$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));
2) ใช้อาร์กิวเมนต์ $ stdin หากคุณต้องการส่งผ่านข้อมูลบางอย่างไปยังอินพุตของโปรแกรมของคุณ:
startBackgroundProcess('cat > input.txt', "Hello world!\n");
คุณอาจลองระบบการเข้าคิวเหมือนResque จากนั้นคุณสามารถสร้างงานที่ประมวลผลข้อมูลและส่งคืนอย่างรวดเร็วด้วยอิมเมจ "กำลังประมวลผล" ด้วยวิธีการนี้คุณจะไม่ทราบว่าเมื่อมันเสร็จสิ้น
โซลูชันนี้มีไว้สำหรับแอปพลิเคชันขนาดใหญ่ที่คุณไม่ต้องการให้เครื่องด้านหน้ายกขึ้นอย่างหนักดังนั้นพวกเขาจึงสามารถดำเนินการตามคำขอของผู้ใช้ได้ ดังนั้นมันอาจหรืออาจไม่ทำงานกับข้อมูลทางกายภาพเช่นไฟล์และโฟลเดอร์ แต่สำหรับการประมวลผลตรรกะที่ซับซ้อนมากขึ้นหรืองานอะซิงโครนัสอื่น ๆ (เช่นอีเมลการลงทะเบียนใหม่) เป็นการดีที่มีและปรับขนาดได้มาก
วิธีแก้ปัญหาการทำงานสำหรับทั้ง Windows และ Linux ค้นหาข้อมูลเพิ่มเติมเกี่ยวกับหน้า GitHub ของฉัน
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;
}
}
}
ขอบคุณคำตอบนี้ : เครื่องมือที่สมบูรณ์แบบสำหรับการเรียกใช้กระบวนการแบ็คกราวน์คือSymfony Process Componentซึ่งขึ้นอยู่กับproc_*
ฟังก์ชั่นการใช้งาน แต่ง่ายกว่ามาก ดูเอกสารประกอบสำหรับข้อมูลเพิ่มเติม
แทนที่จะเริ่มต้นกระบวนการพื้นหลังสิ่งที่เกี่ยวกับการสร้างไฟล์ทริกเกอร์และมีตารางเวลาเช่น cron หรือ autosys รันสคริปต์ที่ค้นหาและดำเนินการกับไฟล์ทริกเกอร์เป็นระยะ? ทริกเกอร์อาจมีคำแนะนำหรือคำสั่งแบบ raw (ยังดีกว่าเพียงแค่ทำให้มันเป็นเชลล์สคริปต์)
หากใช้ PHP มีวิธีที่ง่ายกว่ามากในการใช้ pcntl_fork:
ฉันใช้อย่างหนัก fast_cgi_finish_request()
ใช้ร่วมกับการปิดและ register_shutdown_function ()
$message ='job executed';
$backgroundJob = function() use ($message) {
//do some work here
echo $message;
}
จากนั้นลงทะเบียนการปิดนี้เพื่อดำเนินการก่อนที่จะปิด
register_shutdown_function($backgroundJob);
ในที่สุดเมื่อคำตอบถูกส่งไปยังลูกค้าคุณสามารถปิดการเชื่อมต่อกับลูกค้าและทำงานกับกระบวนการ PHP ต่อไป:
fast_cgi_finish_request();
การปิดจะถูกดำเนินการหลังจาก fast_cgi_finish_request
ข้อความ $ จะไม่ปรากฏตลอดเวลา และคุณสามารถลงทะเบียนการปิดได้มากเท่าที่คุณต้องการ แต่ดูแลเกี่ยวกับเวลาดำเนินการสคริปต์ สิ่งนี้จะใช้ได้ก็ต่อเมื่อ PHP ทำงานเป็นโมดูล Fast CGI (ใช่ไหม?!)
การเขียนสคริปต์ PHP ไม่เหมือนกับภาษาเดสก์ท็อปอื่น ๆ ที่กำลังพัฒนาภาษา ในภาษาแอปพลิเคชันเดสก์ท็อปเราสามารถตั้ง daemon เธรดเพื่อเรียกใช้กระบวนการพื้นหลัง แต่ใน PHP กระบวนการจะเกิดขึ้นเมื่อผู้ใช้ร้องขอหน้า อย่างไรก็ตามมันเป็นไปได้ที่จะตั้งค่างานพื้นหลังโดยใช้ฟังก์ชั่นงาน cron ของเซิร์ฟเวอร์ที่สคริปต์ PHP ทำงาน
สำหรับพวกเราที่ใช้Windowsให้ดูที่:
การอ้างอิง: http://php.net/manual/en/function.exec.php#43917
ฉันก็ปล้ำกับการให้โปรแกรมทำงานในพื้นหลังใน Windows ในขณะที่สคริปต์ทำงานต่อไป วิธีนี้แตกต่างจากโซลูชันอื่น ๆ ที่ช่วยให้คุณสามารถเริ่มโปรแกรมย่อเล็กสุดขยายใหญ่สุดหรือไม่มีหน้าต่างเลย โซลูชันของ llbra @ phpbrasil ทำงานได้ แต่บางครั้งก็สร้างหน้าต่างที่ไม่พึงประสงค์บนเดสก์ท็อปเมื่อคุณต้องการให้ภารกิจซ่อนอยู่
เริ่ม Notepad.exe ย่อเล็กสุดในพื้นหลัง:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe", 7, false);
?>
เริ่มต้นคำสั่งเชลล์ที่มองไม่เห็นในพื้นหลัง:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
?>
เริ่มต้น MSPaint ให้ใหญ่สุดและรอให้คุณปิดก่อนดำเนินการต่อสคริปต์
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("mspaint.exe", 3, true);
?>
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการ Run () ให้ไปที่: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp
URL ที่แก้ไข:
ไปที่https://technet.microsoft.com/en-us/library/ee156605.aspxแทนเนื่องจากไม่มีลิงค์ด้านบนอีกต่อไป
Fatal error: Class 'COM' not found
ฉันรู้ว่ามันเป็นโพสต์เก่าอายุ 100 ปี แต่อย่างไรก็ตามคิดว่ามันอาจจะมีประโยชน์กับใครบางคน คุณสามารถวางภาพที่มองไม่เห็นที่ใดที่หนึ่งบนหน้าเว็บที่ชี้ไปยัง URL ที่จำเป็นต้องเรียกใช้ในพื้นหลังเช่นนี้:
<img src="run-in-background.php" border="0" alt="" width="1" height="1" />