2012-10-15 22 views
0

有沒有更好的方法來重寫此代碼以獲得增強的性能?爲性能標準編寫bash代碼

如果您要獲得一堆IP,系統似乎會掛起。

TMP_PREFIX='/tmp/synd' 
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX" 
BANNED_IP_MAIL=`$TMP_FILE` 
BANNED_IP_LIST=`$TMP_FILE` 
echo "Banned the following ip addresses on `date`" > $BANNED_IP_MAIL 
echo >> $BANNED_IP_MAIL 
BAD_IP_LIST=`$TMP_FILE` 
netstat -ntu | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST 
cat $BAD_IP_LIST 
if [ $KILL -eq 1 ]; then 
    IP_BAN_NOW=0 
    while read line; do 
     CURR_LINE_CONN=$(echo $line | cut -d" " -f1) 
     CURR_LINE_IP=$(echo $line | cut -d" " -f2) 
     if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then 
      break 
     fi 
     IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST` 
     if [ $IGNORE_BAN -ge 1 ]; then 
      continue 
     fi 
     IP_BAN_NOW=1 
     echo "$CURR_LINE_IP with $CURR_LINE_CONN SYN_RECV connections" >> $BANNED_IP_MAIL 
     echo $CURR_LINE_IP >> $BANNED_IP_LIST 
     echo $CURR_LINE_IP >> $IGNORE_IP_LIST 
     if [ $CSF_BAN -eq 1 ]; then 
      $CSF -d $CURR_LINE_IP 
     else 
      $IPT -I INPUT -s $CURR_LINE_IP -j DROP 
     fi 
    done < $BAD_IP_LIST 
    if [ $IP_BAN_NOW -eq 1 ]; then 
     dt=`date` 
       hn=`hostname` 
     if [ $EMAIL_TO != "" ]; then 
      cat $BANNED_IP_MAIL | mail -s "IP addresses banned on $dt $hn" $EMAIL_TO 
     fi 
    fi 
fi 
rm -f $TMP_PREFIX.* 
+0

如果性能真的很重要(我猜想Lua,Ocaml或Python腳本可能運行得更快,特別是因爲它被編譯爲某些字節碼),Bash被定義解釋可能不是正確的工具。 –

+0

如果您有大量數據需要處理,或者您有大量阻塞流程需要管理,那麼Shell對性能非常有用。由於它具有最原生的過程控制機制,只要你知道你想要什麼。 – MeaCulpa

回答

7

當然,有很多方法可以改進,但你應該嘗試找出真正的瓶頸。 (這可能是iptables,在這種情況下,您可能想要嘗試在一次調用中執行所有表更新,而不是一次一次,但我只是猜測而已。)

以下是一些建議;我沒有讀完:

netstat -ntu | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 | 
sort | uniq -c | sort -nr > $BAD_IP_LIST 

如果您只對SYN_RECV狀態下的連接感興趣,爲什麼要列出udp?無論如何,您正在使用三個實用程序(grep,awkcut)來執行一個簡單的面向行的操作。你可能也只是把一切都之一,例如AWK:

awk '$6 == "SYN_RECV" {print substr($5, 1, index($5, ":") - 1)}' 

事實上,你可以做uniquifying和AWK計數以及:

awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]} END{for (i in ip) print ip[i], i}' 

編輯:您還可以篩選按需要在這裏計數:

awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]} 
    END    {for (i in ip) if (ip[i] >= '$NO_OF_CONNECTIONS') print ip[i], i}' 

現在你只需要輸出IP地址,因爲你不再需要在bash腳本中過濾。我不知道這是否比通過排序和uniq再次排序更快,但它可能很好。

while read line; do 
    CURR_LINE_CONN=$(echo $line | cut -d" " -f1) 
    CURR_LINE_IP=$(echo $line | cut -d" " -f2) 
    if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then 
     break 
    fi 

你想從標準輸入讀取兩個字段。爲什麼你不這樣做:

while read CURR_LINE_CONN CURR_LINE_IP IGNORED && 
     ((CURR_LINE_CONN >= NO_OF_CONNECTIONS)); do 

這節省了兩個子殼和兩個切割調用。 (IGNORED在內置的讀取中只是偏執狂,因爲awk只會輸出兩個字段,但這並不是很好的偏執狂,因爲它默默地忽略了錯誤。)

編輯:如上所示,您可以獲得擺脫這裏的考驗。因此,這將僅僅是:

netstat -nt | 
awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]} 
    END { for (i in ip) 
      if (ip[i] >= '$NO_OF_CONNECTIONS') 
       print ip[i], i}' | tee $BAD_IP_LIST 
if ((KILL)); then 
    IP_BAN_NOW=0 
    while read IP IGNORED; do 

下一頁:

IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST` 
    if [ $IGNORE_BAN -ge 1 ]; then 
     continue 
    fi 

grep -c使得grep的讀取整個輸入文件,以獲取計數;你只想知道該IP是否存在。你想grep -q

if $(grep -q -F -x $CURR_LINE_IP $IGNORE_IP_LIST); then continue; fi 

-F告訴grep來解釋圖案作爲字符串而不是一個正則表達式,這是你想要的,因爲否則.是通配符-x告訴grep來整條生產線匹配這是可能的。一個ip是一個前綴或後綴,或者是另一個的後綴,這會導致錯誤的匹配。-F和-x的組合可能會更快一些,因爲grep可以優化匹配很多)

可能還有更多。這是我得到的。

+1

每個使用cut,grep,sed的awk都會低估它的功能。爲此 – MeaCulpa