2012-12-18 48 views
1

我一直面對shell腳本的一個非常奇怪的問題。使用subshel​​l中的尾部連同while/break不會退出循環

下面是這種情況

SCRIPT1(背景產卵) - > SCRIPT2

SCRIPT2具有下面的代碼

function check_log() 
{ 
    logfile=$1 
    tail -5f ${logfile} | while read line 
    do 
     echo $line 
     if echo $line|grep "${triggerword}";then 
     echo "Logout completion detected" 
     start_leaks_detection 
     triggerwordfound=true 
     echo "Leaks detection complete" 
     fi 
     if $triggerwordfound;then 
     echo "Trigger word found and processing complete.Exiting" 
     break 
     fi 

    done 
     echo "Outside loop" 
     exit 0 

} 

check_log "/tmp/somefile.log" "Logout detected" 

現在while循環不利於在這裏休息。我可以看到「註銷完成檢測」以及在標準輸出上回顯「泄漏檢測完成」,但不是字符串「外部循環」

我假設這必須使用tail -f創建子外殼。我想要做的是,退出該子shell以及退出Script2以將控制權返回給Script1。

有人可以請說明如何做到這一點?

+0

當你管道的東西,你隱式創建一個無法修改父母的環境的叉子(小孩)...看看http://stackoverflow.com/questions/13763942/bash-why-piping-input -to-read-only-works-when-fed-into-while-read-const/13764018#13764018 –

+0

錯誤的鏈接@F - 這更關係到http://stackoverflow.com/questions/7178888/grep -q-not-exiting-with-tail -f –

+0

@Neeraj您可能希望使用case語句來比較觸發器字與輸入行,而不是爲每個輸入分支'grep'和'echo'管道線。 –

回答

1

試試這個,雖然它不是完全一樣(它不會跳過在啓動時的日誌文件的開頭):

triggerwordfound= 
while [ -z "$triggerwordfound" ]; do 
    while read line; do 
     echo $line 
     if echo $line|grep "${triggerword}";then 
      echo "Logout completion detected" 
      start_leaks_detection 
      triggerwordfound=true 
      echo "Leaks detection complete" 
     fi 
    done 
done < "$logfile" 
echo "Outside loop" 

雙循環有效地做與tail -f相同的事情。

+0

這應該起作用,但最好使用'case'語句而不是分支'echo'和'grep'。 –

+0

@ GregA.Woods確實。我的第一個方法是'if [[$ line =〜$ triggerword]]'但是我記得他沒有使用bash,並且我沒有修改他的代碼,因爲雙循環部分是最有趣的。其餘的肯定可以改進。 – rici

+0

當然你的意思是要麼'[-z] $ triggerword「]'或'測試-z」$ triggerword「' –

3

相反管道進入while循環,使用這種格式來代替:

while read line 
do 
    # put loop body here 
done < <(tail -5f ${logfile}) 
+0

是雙倍大於符號「<」與空間或是他們「<<」(沒有空間) – Neeraj

+0

有一個空間。 '<(command)'被稱爲進程替換,'<(command)'意味着將進程的輸出重定向到stdin。 – dogbane

+0

當我嘗試這個時,我得到以下錯誤./Automation/check_success.sh:第20行:語法錯誤附近意外的標記'<' ./Automation/check_success.sh:第20行:'\t done <<尾部-5f $ {logfile})' 我正在使用shell,因爲這是在macosx上,只有shell可用。 – Neeraj

0

您的功能在某種意義上是有效的,但您不會注意到它會這樣做,直到之後的另一行寫入文件爲止。這是因爲tail -5 -f通常可以在一個呼叫中將所有文件的最後五行寫入管道,並在一次呼叫中繼續寫入新行,因此它不會發送SIGPIPE信號,直到它嘗試寫入管while循環已退出。

因此,如果您的文件定期增長,那麼應該沒有問題,但如果您的文件在寫入觸發器文字後停止增長更爲常見,那麼您的觀察器腳本也會一直掛起新的輸出寫入文件。

I.e.當管道關閉時,即使有未讀取的數據被緩衝,但只有當管道上的後續write()被嘗試時,纔會立即發送SIGPIPE

這可以很簡單地演示。此命令將不會退出(提供的文件的尾部小於管尺寸的緩衝器),直到手動中斷,或者你寫多一個字節到該文件:

tail -f some_large_file | read one 

但是如果強制尾進行多次寫入管道,並最終寫入前讀者退出確認,那麼一切都將如預期:

tail -c 1000000 some_large_file | read one 

遺憾的是它並不總是容易發現一個給定的系統上的管道緩衝區的大小,當文件中已經存在多於一個管道緩衝區的數據時,也不可能只開始讀文件,並且觸發字已經在文件中,並且至少有一個管道緩衝區的大小爲字節。

不幸的是tail -F(這是你應該使用的而不是-f)也不會嘗試每5秒寫零字節,否則這可能會以更有效的方式解決你的問題。

另外,如果你要堅持使用tail,然後-1可能是足夠的,至少對於檢測到任何未來事件。

順便說一句,這裏有一個略微改進的實現,仍然使用tail,因爲我認爲這可能是你最好的選擇(你可以隨時添加一個週期性的標記線到日誌與cron或類似的(最syslogd實現具有內置馬克功能太),以保證您的功能將標記的時間內返回):

check_log() 
{ 
     tail -1 -F "$1" | while read line; do 
       case "$line" in 
       *"${2:-SOMETHING_IMPOSSIBLE_THAT_CANNOT_MATCH}"*) 
         echo "Found trigger word" 
         break 
         ;; 
       esac 
     done 
} 

更換echo語句需要讀取時觸發詞做任何處理。

+0

當然,這是一個* bashism *,但bash是可移植的,可以在幾乎所有的服務器環境中工作(必須在日誌文件中工作)。在所有'perl'通常是這種日誌文件處理的更好選擇。 –