优化Bash脚本,子shell删除

时间:2014-01-13 04:23:13

标签: linux bash optimization

我有一个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)。

3 个答案:

答案 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的调用中。

如果不出意外,这将意味着您不再产生一个管道,为第一个循环中处理的每一行输入创建新的grepcut进程。

答案 1 :(得分:2)

您可以使用cut -d' ' <<< "$line" | sed ...减少该值。您可以编写更复杂的sed脚本,并避免使用cut

但真正的好处是避免循环,因此只涉及一个sed(或awkperl或...)脚本。我可能希望将其缩减到ipaddresses=$(netstat -ano | awk '...'),这样每行只有一个grepcut,而不是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)。真棒!