我有一个bash脚本,列出端口上连接的IP地址数量。我的问题是,有大量的连接,它就像便便一样慢。我认为这是因为使用了子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)。
答案 0 :(得分:2)
我会抓住几个问题:
如果没有分配合理缓冲区的方法,执行增量字符串连接的脚本中的以下行将不会有效:
ipaddresses="${ipaddresses}${ipaddress}\n"
另一方面,当管道执行时,使用带有while
的{{1}}循环比管道要差得多。尝试这样的事情而不是第一个循环:
read line
此外,尝试将三个连续netstat -ano |
grep -v 'unix' |
grep 'ESTABLISHED' |
grep "\:${portnumber}" |
cut -d' ' -f5 |
sed 's/:[^:]*$//' |
while read line; do ...
命令中的至少两个命令合并到一个grep
的调用中。
如果不出意外,这将意味着您不再产生一个管道,为第一个循环中处理的每一行输入创建新的grep
和cut
进程。
答案 1 :(得分:2)
您可以使用cut -d' ' <<< "$line" | sed ...
减少该值。您可以编写更复杂的sed
脚本,并避免使用cut
。
但真正的好处是避免循环,因此只涉及一个sed
(或awk
或perl
或...)脚本。我可能希望将其缩减到ipaddresses=$(netstat -ano | awk '...')
,这样每行只有一个grep
和cut
,而不是3 sed
个进程,只有一个{{1}过程。
awk
这可能相当笨拙,但它是现有代码的相当直接的音译。请注意将ipaddresses=$(netstat -ano |
awk " /unix/ { next } # grep -v unix
!/ESTABLISHED/ { next } # grep ESTABLISHED
!/:${portnumber}/ { next } # grep :${portnum} "'
{ sub(/:[^:]*$/, "", $5); print $5; }'
)
引入正则表达式的引号。
由于您将IP地址列表提供给${portnumber}
和uniq -c
。您可能应该使用sort -r
,也可以使用sort -rn
来执行awk
。
唯一不容易改进的是uniq -c
;这似乎一次只占用一个主机或IP地址参数,因此您必须为每个名称或地址运行它。
答案 2 :(得分:2)
这是一个优化的整个脚本&amp;重构:
#!/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)。真棒!