簡體   English   中英

優化Bash腳本,刪除子外殼

[英]Optimizing Bash script, subshell removal

我有一個bash腳本,其中列出了端口上連接的IP地址的數量。 我的問題是,連接大量時,它的速度很慢。 我認為這是因為使用了子外殼,但是在不使腳本其余部分枯燥的情況下,我很難刪除它們。 這是完整的腳本,因為它很短:

    #!/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/:[^:]*$// echo ${line} | cut -d' ' -f5 | sed s/:[^:]*$// echo ${line} | cut -d' ' -f5 | sed s/:[^:]*$//是內聯此代碼以生成更快腳本的最佳方法。 擁有1000個並發用戶需要花費一秒鍾的時間(這是我的基本目標,盡管應該能夠處理更多而不會耗盡我的所有CPU)。

我要刺幾個問題:

如果沒有分配合理緩沖區的方法,腳本中執行增量字符串連接的以下行將不會高效:

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

另一個原因是,當管道將要執行操作時,在read line使用while循環會比管道更糟糕。 試試這樣的東西,而不是第一個循環:

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

另外,請嘗試將三個順序grep命令中的至少兩個組合到一次grep調用中。

如果不出意外,這將意味着你不再產卵管道這創造了新的cutsed輸入中的每行第一個循環處理過程。

您可以使用cut -d' ' <<< "$line" | sed ... cut -d' ' <<< "$line" | sed ... 您可以編寫更復雜的sed腳本,並避免使用cut

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

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地址參數,因此您必須為每個名稱或地址運行它。

這是優化和重構的整個腳本:

#!/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)。 太棒了!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM