[英]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
調用中。
如果不出意外,這將意味着你不再產卵管道這創造了新的cut
和sed
輸入中的每行第一個循環處理過程。
您可以使用cut -d' ' <<< "$line" | sed ...
cut -d' ' <<< "$line" | sed ...
您可以編寫更復雜的sed
腳本,並避免使用cut
。
但是真正的好處是避免了循環,因此只涉及一個sed
(或awk
或perl
或…)腳本。 我可能希望將其減少到ipaddresses=$(netstat -ano | awk '...')
以便僅使用一個awk
進程,而不是3個grep
進程,加上每行一個cut
和sed
。
ipaddresses=$(netstat -ano |
awk " /unix/ { next } # grep -v unix
!/ESTABLISHED/ { next } # grep ESTABLISHED
!/:${portnumber}/ { next } # grep :${portnum} "'
{ sub(/:[^:]*$/, "", $5); print $5; }'
)
那可能很笨拙,但這是對現有代碼的相當直接的音譯。 請注意報價,以獲取${portnumber}
進入正則表達式。
由於您將IP地址列表輸入uniq -c
和sort -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.