Not familiar with Linux Shell programming, I used PHP to write a multi-process parallel framework that can be run on a terminal. The function is very simple. It can basically handle some time-consuming tasks in batches in Linux (correct some errors in the first version and make some optimizations). Address: www. oschina. netcodesnippet_779765_000045 no #! Usr
Not familiar with Linux Shell programming, I used PHP to write a multi-process parallel framework that can be run on a terminal. Function is very simple, can basically meet the Linux at the same time batch processing some time-consuming work (correct some errors on the first version, and some optimization) Address of the first version: http://www.oschina.net/code/snippet_779765_44045 #! /Usr/
Not familiar with Linux Shell programming, I used PHP to write a multi-process parallel framework that can be run on a terminal. The function is very simple and can basically meet the needs of Linux to handle some time-consuming tasks in batch at the same time (correct some errors on the first version and make some optimizations)
Address of the first version: http://www.oschina.net/code/snippet_779765_44045 <无>
#! /Usr/bin/env php
** The bat-test.php script content is as follows:
159);} function B ($ line) {do {bat: Y ("I am passing the Parameter \ $ line = $ line"); usleep (500000 );} while (mt_rand (100,999)> 359);} function c () {global $ x; bat: notify ("the initial variable values between multiple tasks are not affected, \ $ x = $ x "); bat: notify (" I'm suspending the 9-second test "); sleep (9); bat :: Y ("I'm an error code 5 test"); exit (5) ;}?> * // ** Make sure that this script can only run in SHELL */if (substr (php_sapi_name (), 0, 3 )! = 'Cli ') {die ("This Programe can only be run in cli mode. \ n");} if (! Is_callable ('pcntl _ fork') |! Is_callable ('msg _ send') {bat: message ("this program requires pcntl, sysvmsg extension, but your system is not installed! ", 2); exit (5);} class bat {static private $ max = 3, $ total = 0, $ running = 0, $ failure = 0, $ finished = 0, $ daemon = false, $ tasks = array (), $ msg, $ msgs = array (), $ logfile = "/tmp/bat. php. log ", $ childs, $ start, $ split, $ get, $ parent, $ wait; static function init () {set_time_limit (0); error_reporting (8106 & E_ALL ); ini_set ('display _ errors ', 'off'); set_error_handler (array (_ CLASS __, 'error'), E_ALL); set_exception_ha Ndler (array (_ CLASS __, 'exception'); register_shutdown_function (array (_ CLASS __, 'shutdown '); self: $ start = time (); self: $ split = str_repeat ('=', 512); self: $ parent = msg_get_queue (getmypid ();} static function main ($ inc) {self :: init (); ob_get_level () & ob_end_clean (); if ($ _ SERVER ["argc"] = 1) {if ($ inc) return true; self :: usage () ;}$ I = 0; $ daemon = false; $ files = array (); while (++ $ I <$ _ SERVER ["argc"]) {Switch ($ _ SERVER ["argv"] [$ I]) {case "? ": Case "/? ": Case "-? ": Case"-h ": case" -- help ": self: usage (); case"-f ": case" -- file ": if (++ $ I <$ _ SERVER ["argc"]) {$ file = $ _ SERVER ["argv"] [$ I]; if (is_readable ($ file) {$ files [] = $ file; continue;} self: message ("script $ file is not in or inaccessible", 2 ); self: end (4);} self: message ("script parameter missing", 1); echo "\ n please use $ _ ENV [_] -- help to view help! \ N "; self: end (1); case"-m ": case" -- max ": if (++ $ I <$ _ SERVER [" argc "]) {self: $ max = intval ($ _ SERVER ["argv"] [$ I]); if (self: $ max >=1) continue; self :: message ("Number of processes should be a positive integer", 2); self: end (8);} self: message ("Number of unspecified processes", 2); self :: end (7); case "-l": case "-- log": case "-- logfile": if (++ $ I <$ _ SERVER ["argc"]) {if (is_dir (self: $ logfile = $ _ SERVER ["argv"] [$ I]) {self ::$ logfile. = "/bat. php. log ";} if (is_file (self: $ logfile) {If (is_writable (self: $ logfile) {continue ;}} else {if (is_writable (dirname (self: $ logfile) {continue ;}} self: message ("log directory cannot be written", 2); self: end (9);} self: message ("log file/directory missing", 2 ); self: end (6); case "-d": case "-- daemon": $ daemon = true; continue; case "-v": case "-- version ": self: $ daemon = true; exit (self: version (); default: $ file = $ _ SERVER ["argv"] [$ I]; if (is_readable ($ file) {$ files [] = $ file; continue;} else If ($ file [0] = '-') {self: message ("Unrecognized options $ file", 1);} else {self :: message ("the script $ file is not in or cannot be accessed", 2);} self: end (4) ;}}$ daemon & self: daemon (); foreach ($ files as $ file) {self: inc ($ file) ;}} static function run ($ fun, $ arg = null) {if (is_callable ($ fun) {self: $ tasks [] = array ($ fun, $ arg );} else {throw new Exception ("not a function or cannot be called", 9) ;}} static function start () {self ::$ total + = count (self :: $ tasks); foreach (self: $ tas Ks as $ fun_arg) {if (self ::$ max <+ self ::$ running) {self: run_wait ($ cid);} elseif (self :: $ running = 1) {if (! Self: $ daemon) {# Clear the screen and set the cursor to the first line $ x = intval ('tput lines'); echo str_repeat ("\ n ", $ x-1); self: flush ('program starts to execute... ', 1) ;}self: $ wait = 0;} if ($ cid = pcntl_fork () {if ($ cid <0) {throw new Exception ("failed to create process", 3);} self ::$ childs [$ cid] = msg_get_queue ($ cid);} else {ob_start (); self:: $ tasks = array (); self: $ get = msg_get_queue (getmypid (); self: $ msg = sprintf ("%-6d", getmypid ()); msg_receive (self: $ get, 0, $ typ, 128, $ Msg); msg_send (self: $ parent, 1, getmypid (), false); call_user_func ($ fun_arg [0], $ fun_arg [1]); exit ;}} while (self ::$ running) self: run_wait ($ cid); self ::$ tasks = array ();} static function daemon () {if ($ cid = pcntl_fork () {if ($ cid <0) {throw new Exception ("process creation failed", 3);} self :: $ daemon = true; exit ("PID: $ cid \ n");} self: $ daemon = true;} static function is_da () {return self :: $ daemon;} static function set_max ($ max) {$ Max = intval ($ max); if ($ max> 0) self ::$ max = $ max;} static function set_logfile ($ log) {if (is_dir ($ log) {$ log. = "/bat. php. log ";} if (is_file ($ log) {if (is_writable ($ log) {self ::$ logfile = $ log ;}} else {if (is_writable (dirname ($ log) {self ::$ logfile = $ log ;}} static private function run_exit ($ cid, $ status) {if (! Pcntl_wifexited ($ status) | pcntl_wexitstatus ($ status) {$ msg = sprintf ("%-6d % s", $ cid, date ("H: I: s "), 'process exits abnormally '); if (pcntl_wifexited ($ status) {$ msg. = ', error code :'. pcntl_wexitstatus ($ status);} self: $ failure ++;} else {$ msg = sprintf ("%-6d % s", $ cid, date ("H: I: s"), 'process execution completed '); self ::$ finished ++;} unset (self ::$ childs [$ cid]); self: $ daemon | self: flush ($ msg, 4); self: $ running --;} static private function ru N_wait ($ cid) {static $ childs = array (); if (self ::$ wait) {if (self ::$ wait! = $ Cid) {# $ childs [$ cid] = time (); msg_send (self: $ childs [$ cid], 1, 'start ');}} else {foreach (self ::$ childs as $ cid =>$ t) {msg_send ($ t, 1, 'start '); # $ childs [$ cid] = time () ;}} self ::$ wait = $ cid; label_time: $ nomsg_interval = time () + 6; label_wait: $ time = time (); if (msg_receive (self: $ parent, 0, $ typ, 8192, $ msg, false, MSG_NOERROR | MSG_IPC_NOWAIT )) {if ($ typ = 3) {self: $ daemon | self: flush ($ msg, $ time);} e Lse {if ($ typ = 1) {$ msg = sprintf ("%-6d % s", $ msg, date ("H: I: s "), 'process start'); self: $ daemon | self: flush ($ msg, 3);} elseif ($ typ = 4) {if ($ cid = pcntl_waitpid ($ msg, $ status, WNOHANG) and $ cid> 0) {self: run_exit ($ cid, $ status); return ;}} elseif ($ typ = 5 & is_callable ("bat_diy_notify") {bat_diy_notify ($ msg) ;}} else {if (self: $ daemon) {if ($ cid = pcntl_wait ($ status, WNOHANG) and $ cid> 0) {self: run_exit ($ Cid, $ status); return;} else {sleep (6); goto label_time ;}} elseif (self: flush (null, 5 )) {echo "\ 33 [0; 0 H"; $ lines = intval ('tput lines'); echo "\ 33 [K running Duration:", self :: run_time (), '', date (" Y-m-d H: I: s ", self: $ start ),'-', date ("Y-m-d H: I: s"), "\ 33 [$ lines; 0 H" ;}usleep (200000 );} if ($ nomsg_interval <$ time) {foreach (self ::$ childs as $ msg =>t t) {if ($ cid = pcntl_waitpid ($ msg, $ status, WNOHANG) and $ cid> 0) {self:: Run_exit ($ cid, $ status) ;}} if (self ::$ max> self ::$ running) return; goto label_time ;}goto label_wait ;} static function notify ($ msg, $ diy = false) {if ($ diy) {if (self ::$ get) {msg_send (self ::$ parent, 5, $ msg, false);} else {bat_diy_y y (string) $ msg) ;}} else {if (self ::$ daemon) {# msg_send (self ::$ parent, 5, self ::$ msg, false);} else {msg_send (self ::$ parent, 3, self ::$ msg. date ("H: I: s "). $ msg, false) ;}} static Function message ($ msg, $ code = 0) {if (self: $ daemon) {return true;} switch ($ code) {case 0: echo "\ 33 [37m prompt: \ 33 [0 m", $ msg, "\ n"; break; case 1: echo "\ 33 [33m warning: \ 33 [0 m ", $ msg," \ n "; break; case 2: echo" \ 33 [31m error: \ 33 [0 m ", $ msg, "\ n"; break ;}} static function confirm ($ msg = "OK to continue executing") {if (self: $ daemon) return true; # echo $ msg by default in the background, "(yes/no )?: "; # Return" yes \ n "= fgets (STDIN);} static function usage () {$ bat = _ CLASS __; echo "", "Usage: \ n", "$ _ ENV [_] [options] [-f | -- file]
\ N "," Options: \ n ","-h | -- help: display the help information \ n ","-v | -- version: view the program version \ n ", "\ n", "-m | -- max
Number of processes simultaneously executed. Default Value: ", self: $ max," \ n ","-l | -- log
Error Log File. Default Value: ", self: $ logfile," \ n ","-d | -- daemon starts background process execution and does not occupy terminal and output content \ n ", "Information: \ n", "$ bat: run (fun [, arg]) in the script to add task \ n", "fun is the name of the function to be executed; arg is the parameter passed to this function. It can save \ n "," \ n "," $ bat: start () called in the script () run the preceding task \ n "," in the child process, send the information to be displayed to the parent process \ n by calling $ bat: Running y (msg ", "In a sub-process, an error occurs in program execution. To make the main process statistics fail, exit (num) non-zero return \ n", "\ n ", "You can provide a bat_diy_notify (msg) function in the main process to record the data returned by the sub-process \ n", "the sub-process calls $ bat: notify (msg, true) to pass Send data to the parent process. The difference is that the true Parameter \ n is added. "NOTE: msg can only be a string and cannot be too long, to avoid unnecessary parts being discarded by the System \ n "; self: $ daemon = true; exit ();} static function version () {return" Version: 0.3 by huye \ n ";} static private function inc ($ file) {include $ file;} static private function end ($ code = 0) {if ($ code | self ::$ daemon) {# echo self ::$ total, "task completed \ n"; self ::$ daemon = true; exit ($ code) ;}$ cols = intval ('tput cols'); $ lines = intval ('tput lines'); if (self: $ Total) {self: flush ("execution completed. ", 2); echo" \ 33 [$ lines; {$ cols} H \ 33 [1C \ n ";} if (is_file (self: $ logfile) & filemtime (self ::$ logfile) >=self ::$ start) {echo "\ 33 [K error:", self ::$ logfile, "\ n";} echo "\ 33 [K running Duration:", self: run_time (), '', date (" Y-m-d H: I: s ", self: $ start), '-', date (" Y-m-d H: I: s ")," \ n "; echo "\ 33 [K execution completed: completed task", self ::$ finished, "Count", self ::$ failure? ", Failed". self: $ failure. ":" ", self: $ total? "(Total". self: $ total )":"",". \ N ";} static private function flush ($ msg, $ time) {$ cols = intval ('tput cols'); $ lines = intval ('tput lines '); if ($ msg) {$ _ max = $ cols; foreach (explode ("\ n", $ msg) as $ msg) {if ($ cols <strlen ($ msg) {# ascii utf8 ascii utf8 ascii... $ tmp = preg_split ("#((?: [\ Xe0-\ xef] [\ x80-\ xbf] {2}) +) # ", $ msg, 0, PREG_SPLIT_DELIM_CAPTURE); for ($ I = 0, $ l = count ($ tmp); $ I <$ l;) {$ x = strlen ($ z = $ tmp [$ I]); if ($ _ max >$ x) {_ _ max-= $ x; if (++ $ I >=$ l) break; $ x = strlen ($ z = $ tmp [$ I])/3*2; if ($ _ max> $ x) {$ _ max-= $ x; $ I ++; continue;} elseif ($ _ max <$ x) {$ _ max = floor ($ _ max/2) * 3; $ msg = array_slice ($ tmp, $ I-1); $ msg [0] = ''; $ msg [1] = substr ($ z, $ _ max ); $ tmp [$ I] = substr ($ z, 0, $ _ max);} else {$ msg = array_slice ($ tmp, $ I + 1) ;}} elseif ($ _ max <$ x) {$ msg = array_slice ($ tmp, $ I); $ msg [0] = substr ($ z, $ _ max ); $ tmp [$ I] = substr ($ z, 0, $ _ max);} else {$ msg = array_slice ($ tmp, $ I ); $ msg [0] = '';} if (++ $ I <$ l) {array_splice ($ tmp, $ I );} if (isset ($ msg [1]) {self: $ msgs [] = implode ("", $ tmp); $ msg [0] = "". $ msg [0]; $ I = 0; $ l = count ($ msg); $ tmp = $ msg; $ _ max = $ cols ;} elseif (isset ($ msg [0]) & Strlen ($ msg [0]) {self: $ msgs [] = implode ("", $ tmp ); if ($ cols-15 <strlen ($ msg [0]) {foreach (str_split ($ msg [0], $ cols-15) as $ tmp) {$ tmp = "". $ tmp; if ($ cols = strlen ($ tmp) {self ::$ msgs [] = $ tmp;} else {$ tmp = array ($ tmp ); break ;}} else {$ tmp = array ("". $ msg [0]);} break;} else {break;} self: $ msgs [] = implode ("", $ tmp);} else {self :: $ msgs [] = $ msg ;}} elseif ($ msg! = Null) {self ::$ msgs [] = $ msg;} static $ last_time = 0; if ($ last_time = $ time) return true; $ last_time = $ time; # prevent screen flushing during remote ssh. echo "\ 33 [0; 0 H"; # echo "\ 33 [K program information:", self: version (); echo "\ 33 [K running Duration:", self: run_time (), '', date (" Y-m-d H: I: s ", self :: $ start), '-', date ("Y-m-d H: I: s"), "\ n"; if ($ lines <5) {if ($ lines <3) return;} else {echo $ split = substr (self: $ split, 0, $ cols), "\ n "; if ($ _ max = count (s Elf: $ msgs) + 4) >lines lines) {array_splice (self: $ msgs, 0, $ _ max-$ lines );} elseif ($ lines >$ _ max) {$ split = str_repeat ("\ n \ 33 [K", $ lines-$ _ max ). $ split;} echo "\ 33 [K", implode ("\ n \ 33 [K", self: $ msgs), "\ n", $ split, "\ n" ;}$ msg = "completed task ". self: $ finished. "; if (self: $ failure) $ msg. = ", failed ". self: $ failure. "; if (self: $ total) $ msg. = "(total ". self: $ total. ")"; echo str_repeat ('', $ cols -Strlen (preg_replace ("# [\ xe0-\ xef] [\ x80-\ xbf] {2} #", "**", $ msg ))), $ msg, "\ 33 [$ lines; 0 H";} static function run_time () {$ consume = time ()-self: $ start; $ str = ""; if ($ consume >=86400) {$ str = floor ($ consume/86400 ). "Day"; $ consume = $ consume % 86400; $ zero = true;} if ($ consume >=3600) {$ str. = floor ($ consume/3600 ). "Hour"; $ consume = $ consume % 3600; $ zero = true;} elseif ($ consume> 0 & isset ($ zero) {un Set ($ zero); $ str. = "zero";} if ($ consume >=60) {$ str. = floor ($ consume/60 ). "Points"; $ consume = $ consume % 60; $ zero = true;} elseif ($ consume> 0 & isset ($ zero) {unset ($ zero ); $ str. = "0";} if ($ consume> 0) {$ str. = $ consume. "seconds";} elseif ($ str = "") {$ str = "0 seconds";} return $ str;} static function error ($ no, $ err, $ file, $ line) {if (error_reporting () {$ log =no no & 1032? & Apos; M & apos;: ($ no & 514? 'W': ($ no & 2048? 'M': 'E'); $ log = "[". date ("m-d H: I: s "). "] $ log $ line $ file $ err \ n"; file_put_contents (self ::$ logfile, $ log, FILE_APPEND) ;}} static function shutdown () {if ($ last = error_get_last () and 85 & $ last ['type']) {self: error ($ last ['type'], $ last ['message'], $ last ['file'], $ last ['line']); self: $ get | self: end (); self: $ daemon = true;} if (self: $ get) {msg_send (self: $ parent, 4, getmypid (), false ); # notify the parent process to end self ::$ parent = self ::$ get; # It also prevents the failure of the previous row of notification ob_end_clean () ;} else {self: end ();} msg_remove_queue (self: $ parent);} static function exception ($ e) {self: error ($ e-> getCode (), $ e-> getMessage (), $ e-> getFile (), $ e-> getLine (); exit ($ e-> getCode () ;}} bat: main (debug_backtrace ());