2013-05-27 85 views
2

我必須運行Bash命令。但是這個命令需要幾分鐘才能運行。從Perl腳本異步執行Bash命令

如果我正常(同步)執行此命令,我的應用程序將掛起,直到命令完成運行。

如何從Perl腳本異步運行Bash命令?

+1

請參閱[我如何在Perl中觸發並忘記進程?](http://stackoverflow.com/questions/2133910/how-can-i-fire-and-forget-a-process-in-perl) – devnull

+0

你關心bash輸出嗎? –

+0

是的。我想要結果,什麼和什麼時候?像這樣:當命令完成時,它會提醒我的應用程序,如事件觸發器。 –

回答

2

您可以使用threads異步開始猛砸,

use threads; 
my $t = async { 
    return scalar `.. long running command ..`; 
}; 

後來手動測試,如果線程準備加入,並在非阻塞的方式得到輸出,

my $output = $t->is_joinable() && $t->join(); 
+0

+1。我有一個小問題。如果在'async'行使用'$ t'後,perl會返回一個錯誤:'全局符號'$ t「需要顯式包名'。在第一次使用'$ t'之前,'sleep 1;'解決了這個問題,但是有沒有適當的方法來處理這個問題呢? – TrueY

+0

你能複製粘貼你的代碼嗎? –

+0

''my $ t = async {return'。/ wait.sh'; }''print print $ t;'。如果在兩條線之間添加「睡眠1」,則它可以正常工作。 – TrueY

2

如果您不關心結果,您可以使用system("my_bash_script &");。它會立即返回,腳本完成需要完成的任務。

我有兩個文件:

$ cat wait.sh 
#!/usr/bin/bash 
for i in {1..5}; { echo "wait#$i"; sleep 1;} 

$cat wait.pl 
#!/usr/bin/perl 
use strict; use warnings; 
my $t = time; 
system("./wait.sh"); 
my $t1 = time; 
print $t1 - $t, "\n"; 
system("./wait.sh &"); 
print time - $t1, "\n"; 

輸出:

wait#1 
wait#2 
wait#3 
wait#4 
wait#5 
5 
0 
wait#1 
wait#2 
wait#3 
wait#4 
wait#5 

可以看出,第二個電話會立即返回,但它一直寫到標準輸出。

如果您需要與孩子溝通,則需要使用fork並重定向STDINSTDOUT(和STDERR)。或者您可以使用IPC::Open2IPC::Open3包。無論如何,在呼叫者退出之前等待孩子退出總是一個好習慣。

如果你想等待執行的過程中,你可以嘗試使用Bash是這樣的:

#!/usr/bin/bash 

cpid=() 
for exe in script1 script2 script3; do 
    $exe& 
    cpid[$!]="$exe"; 
done 

while [ ${#cpid[*]} -gt 0 ]; do 
    for i in ${!cpid[*]}; do 
    [ ! -d /proc/$i ] && echo UNSET $i && unset cpid[$i] 
    done 
    echo DO SOMETHING HERE; sleep 2 
done 

這個腳本起初異步啓動腳本#並存儲在陣列中的PID被稱爲cpid。然後是一個循環;它瀏覽它們仍在運行(/ proc/exists)。如果不存在,則顯示文本UNSET <PID>,並從陣列中刪除PID。

它不是防彈的,就好像DO SOMETHING HERE零件運行時間很長,那麼相同的PID可以被添加到另一個過程中。但它在平均環境中效果很好。

但這種風險也可降低:

#!/usr/bin/bash 

# Enable job control and handle SIGCHLD 
set -m 
remove() { 
    for i in ${!cpid[*]}; do 
    [ ! -d /proc/$i ] && echo UNSET $i && unset cpid[$i] && break 
    done 
} 
trap "remove" SIGCHLD 

#Start background processes 
cpid=() 
for exe in "script1 arg1" "script2 arg2" "script3 arg3" ; do 
    $exe& 
    cpid[$!]=$exe; 
done 

#Non-blocking wait for background processes to stop 
while [ ${#cpid[*]} -gt 0 ]; do 
    echo DO SOMETHING; sleep 2 
done 

該版本允許腳本時的異步子進程退出接收SIGCHLD信號。如果收到SIGCHLD,它會異步查找第一個不存在的進程。等待while循環減少了很多。

+0

另一個選項是'open(my $ fh,'| - ','wait.sh',...)'或'open(my $ fh,' - ','wait.sh',... )'如果你想讀取它的輸出或寫入它的輸入,但不是兩者。 – reinierpost

+0

不,你不瞭解我@TrueY,我的bash命令需要很多時間才能運行,所以我希望當bash命令執行時,我的應用程序顯示文本「正在執行...」,並在完成命令後,系統triiger(通知)我的應用程序顯示「執行完成!」。 –

+0

@ThếAnhNguyễn:對不起,我的誤解,但你的描述不包含太多的信息。我有一個[所以回答其他問題](http://stackoverflow.com/questions/16772186/bash-parallelize-md5sum-checksum-on-many-files/16773505#16773505)。它在bash中運行特定數量的並行程序。我根據您的需求定製它。 – TrueY

2

正常這樣做的方式是fork。您將獲得腳本分支,然後子代會在Bash腳本上調用execsystem(取決於孩子是否需要處理Bash腳本的返回代碼,或者與其交互)。

然後,您的父母可能需要wait和/或SIGCHILD處理程序的組合。

如何處理它的確切細節取決於您的情況和確切的需求。