2014-01-13 84 views
1

我有一个bash脚本,列出了在一个端口上连接的ip地址的数量。我的问题是,有大量的连接,它是缓慢的作为便便。我认为这是因为使用了子壳体,但是如果不删除脚本的其余部分,我就会遇到问题。下面是全部脚本,因为它是相当短的:优化Bash脚本,删除子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),需要一秒多。

+2

这不是一个不合理的脚本。尝试从命令行使用'-v'选项运行到'bash' - 例如'bash -v script-name'。该选项将在读取每行时打印出来。看看是否有任何明显的延迟发生在屏幕上打印一行后。那将是开始在脚本中查找性能问题的地方。 – Ned

+0

啊哈,谢谢你,我不知道-v开关。所以,问题在于我相信的子壳 - 特别是这一行:echo $ {line} |剪下-d''-f5 | sed s /:[^:] * $ // – jett

+0

我明白@ElliottFrisch,你是对的。 – jett

回答

2

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

但真正的好处是避免循环,因此只涉及一个sed(或awkperlperl或......)脚本。我可能会希望将其减少到ipaddresses=$(netstat -ano | awk '...'),以便每行代替3个grep进程,再加上一个cutsed,但只有一个进程awk

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地址参数,因此您必须为每个名称或地址运行它。

+0

是的,'awk'将是替换应用于'netstat'输出的第一个循环中的所有内容的一个非常好的选择。这包括'grep'的三次调用... – Ned

+0

非常有趣,与以前相比,速度非常快。学习一些awk的时间! – jett

+1

用一条流程替换所有生产线的每条生产线或三条生产线几乎总是一个巨大的成就。我不能想到很多情况下,这不会是一个大胜利。 –

2

我将在一对夫妇的问题采取了刺:

从增量执行字符串连接不会是有效的,没有手段来分配合理的缓冲脚本中的以下行:

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

对于另一种情况,使用while循环与read line时,管道将做比管道明显更差。尝试这样的事情不是第一个循环的:

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

另外,尽量至少两个三个连续grep命令组合成的grep一个调用。

如果没有别的,这将意味着您不再产生管道,该管道会为第一个循环中处理的每一行输入创建新的cutsed进程。

+0

也许我在这里误解了你,但据我可以告诉管道进入方法实际上不工作?它只是一个重构或者它应该如何工作? – jett

+1

@jett无论你使用管道重定向到一个Unix shell'while while循环 - 即'|' - 或者另一个像'''的流重定向没有区别。这是Unix shell,管道和标准输入,输出和错误的一个特性。 – Ned

+1

@jett就脚本中的任何“subshel​​l”性能而言,你可以做的最好的事情就是使用管道,而不是为每一行输入产生新的Unix进程。 “while”循环的主体只能使用Bash内置命令,或者循环体中执行的每行工作应该足够重要,以保证所用资源的合理性。但是,如果可以使用管道而不是'while'循环来使用它。由于条件逻辑的要求,即使您需要使用临时文件作为缓冲区,也可以使用它。 – Ned

2

这里是优化整个脚本&重构:

#!/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的)。真棒!