2014-01-13 83 views
1

我有一個bash腳本,列出了在一個端口上連接的ip地址的數量。我的問題是,有大量的連接,它是緩慢的作爲便便。我認爲這是因爲使用了子殼體,但是如果不刪除腳本的其餘部分,我就會遇到問題。下面是全部腳本,因爲它是相當短的:優化Bash腳本,刪除子shell

#!/bin/bash 

    portnumber=80 
    reversedns_enabled=0 

    [ ! -z "${1}" ] && portnumber=${1} 
    [ ! -z "${2}" ] && reversedns_enabled=${2} 

    #this will hold all of our ip addresses extracted from netstat 
    ipaddresses="" 

    #get all of our connected ip addresses 
    while read line; do 
      ipaddress=$(echo ${line} | cut -d' ' -f5 | sed s/:[^:]*$//) 
      ipaddresses="${ipaddresses}${ipaddress}\n" 
    done < <(netstat -ano | grep -v unix | grep ESTABLISHED | grep \:${portnumber}) 

    #remove trailing newline 
    ipaddresses=${ipaddresses%%??} 

    #output of program 
    finaloutput="" 

    #get our ip addresses sorted, uniq counted, and reverse sorted based on amount of uniq 
    while read line; do 
      if [[ ${reversedns_enabled} -eq 1 ]]; then 
        reversednsname=""  

        #we use justipaddress to do our nslookup(remove the count of uniq) 
        justipaddress=$(echo ${line} | cut -d' ' -f2) 
        reversednsstring=$(host ${justipaddress}) 
        if echo "${reversednsstring}" | grep -q "domain name pointer"; then 
          reversednsname=$(echo ${reversednsstring} | grep -o "pointer .*" | cut -d' ' -f2) 
        else 
          reversednsname="reverse-dns-not-found" 
        fi 

        finaloutput="${finaloutput}${line} ${reversednsname}\n" 
      else 
        finaloutput="${finaloutput}${line}\n" 
      fi 
    done < <(echo -e ${ipaddresses} | uniq -c | sort -r) 

    #tabulate that sheet son 
    echo -e ${finaloutput} | column -t 

大部分所用的時間做這個操作:echo ${line} | cut -d' ' -f5 | sed s/:[^:]*$//什麼是內聯這產生更快的腳本的最佳方式。它需要1000多個併發用戶(這是我的基本目標,儘管應該能夠處理更多,而不用我所有的CPU),需要一秒多。

+2

這不是一個不合理的腳本。嘗試從命令行使用'-v'選項運行到'bash' - 例如'bash -v script-name'。該選項將在讀取每行時打印出來。看看是否有任何明顯的延遲發生在屏幕上打印一行後。那將是開始在腳本中查找性能問題的地方。 – Ned

+0

啊哈,謝謝你,我不知道-v開關。所以,問題在於我相信的子殼 - 特別是這一行:echo $ {line} |剪下-d''-f5 | sed s /:[^:] * $ // – jett

+0

我明白@ElliottFrisch,你是對的。 – jett

回答

2

您可以使用cut -d' ' <<< "$line" | sed ...來減少此值。您可以編寫更復雜的sed腳本並避免使用cut

但真正的好處是避免循環,因此只涉及一個sed(或awkperlperl或......)腳本。我可能會希望將其減少到ipaddresses=$(netstat -ano | awk '...'),以便每行代替3個grep進程,再加上一個cutsed,但只有一個進程awk

ipaddresses=$(netstat -ano | 
       awk " /unix/   { next } # grep -v unix 
        !/ESTABLISHED/ { next } # grep ESTABLISHED 
        !/:${portnumber}/ { next } # grep :${portnum} "' 
            { sub(/:[^:]*$/, "", $5); print $5; }' 
      ) 

這可能相當笨拙,但它是現有代碼的相當直接的音譯。注意引號將${portnumber}轉換爲正則表達式。

由於您將IP地址列表填入uniq -csort -r。你可能應該使用sort -rn,你也可以使用awk來做uniq -c

唯一不容易改善的是host;似乎一次只需要一個主機或IP地址參數,因此您必須爲每個名稱或地址運行它。

+0

是的,'awk'將是替換應用於'netstat'輸出的第一個循環中的所有內容的一個非常好的選擇。這包括'grep'的三次調用... – Ned

+0

非常有趣,與以前相比,速度非常快。學習一些awk的時間! – jett

+1

用一條流程替換所有生產線的每條生產線或三條生產線幾乎總是一個巨大的成就。我不能想到很多情況下,這不會是一個大勝利。 –

2

我將在一對夫婦的問題採取了刺:

從增量執行字符串連接不會是有效的,沒有手段來分配合理的緩衝腳本中的以下行:

ipaddresses="${ipaddresses}${ipaddress}\n" 

對於另一種情況,使用while循環與read line時,管道將做比管道明顯更差。嘗試這樣的事情不是第一個循環的:

netstat -ano | 
grep -v 'unix' | 
grep 'ESTABLISHED' | 
grep "\:${portnumber}" | 
cut -d' ' -f5 | 
sed 's/:[^:]*$//' | 
while read line; do ... 

另外,儘量至少兩個三個連續grep命令組合成的grep一個調用。

如果沒有別的,這將意味着您不再產生管道,該管道會爲第一個循環中處理的每一行輸入創建新的cutsed進程。

+0

也許我在這裏誤解了你,但據我可以告訴管道進入方法實際上不工作?它只是一個重構或者它應該如何工作? – jett

+1

@jett無論你使用管道重定向到一個Unix shell'while while循環 - 即'|' - 或者另一個像'''的流重定向沒有區別。這是Unix shell,管道和標準輸入,輸出和錯誤的一個特性。 – Ned

+1

@jett就腳本中的任何「subshel​​l」性能而言,你可以做的最好的事情就是使用管道,而不是爲每一行輸入產生新的Unix進程。 「while」循環的主體只能使用Bash內置命令,或者循環體中執行的每行工作應該足夠重要,以保證所用資源的合理性。但是,如果可以使用管道而不是'while'循環來使用它。由於條件邏輯的要求,即使您需要使用臨時文件作爲緩衝區,也可以使用它。 – Ned

2

這裏是優化整個腳本&重構:

#!/bin/bash 

portnumber=80 
reversedns_enabled=0 

[[ $1 ]] && portnumber=$1 
[[ $2 ]] && reversedns_enabled=$2 

#this will hold all of our ip addresses extracted from netstat 
ipaddresses='' 

#get all of our connected ip addresses 
while IFS=' :' read -r type _ _ _ _ ipaddress port state _; do 
    if [[ $type != 'unix' && $port == "$portnumber" && $state == 'ESTABLISHED' ]]; then 
     ipaddresses+="$ipaddress\n" 
    fi 
done < <(netstat -ano) 

#remove trailing newline 
ipaddresses=${ipaddresses%%??} 

#output of program 
finalOutput="" 

#get our ip addresses sorted, uniq counted, and reverse sorted based on amount of uniq 
while read -r line; do 
    if ((reversedns_enabled == 1)); then 
     reverseDnsName="" 

     #we use justipaddress to do our nslookup(remove the count of uniq) 
     read -r _ justipaddress _ <<< "$line" 
     reverseDnsString=$(host "$justipaddress") 
     if [[ $reverseDnsString == *'domain name pointer'* ]]; then 
      reverseDnsName=${reverseDnsName##*domain name pointer } 
     else 
      reverseDnsName="reverse-dns-not-found" 
     fi 

     finalOutput+="$line $reverseDnsName\n" 
    else 
     finalOutput+="$line\n" 
    fi 
done < <(echo -e "$ipaddresses" | sort -ur) 

#tabulate that sheet son 
echo -e "$finalOutput" | column -t 

正如你所看到的,有使用幾乎沒有外部工具(SED沒有,AWK或者grep的)。真棒!