2014-01-13 2 views
1

У меня есть скрипт bash, в котором перечисляется количество ip-адресов, подключенных к порту. Моя проблема в том, что с большим количеством подключений она медленнее, чем poo. Я думаю, что это из-за использования подоболочек, но у меня возникли проблемы с их устранением, не откладывая остальную часть скрипта. Вот сценарий в полном объеме, как это довольно короткий:Оптимизация сценария Bash, удаление подоболочки

#!/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 одновременных пользователей (это моя базовая цель, хотя и должна обрабатывать больше, не используя весь мой процессор).

+2

Это не необоснованный сценарий. Попробуйте запустить его из командной строки, используя опцию '-v' для' bash' - например. 'bash -v script-name'. Этот параметр будет печатать каждую строку по мере ее чтения. Посмотрите, произошла ли какая-либо заметная задержка сразу после печати строки на экране. Это будет место для поиска проблем с производительностью в вашем скрипте. – Ned

+0

Ага, спасибо за это, я не знал о переключателе -v. Итак, проблема связана с подоболочками, которые, я считаю, эта строка в частности: echo $ {line} | cut -d '' -f5 | sed s /: [^:] * $ // – jett

+0

Я ценю, что @ElliottFrisch, вы правы. – jett

ответ

2

Вы можете уменьшить это с помощью cut -d' ' <<< "$line" | sed .... Вы можете написать более сложный сценарий sed и избегать использования cut.

Но реальная выгода будет заключаться в том, чтобы избежать цикла, так что есть только один sed (или awk или perl или ...) скрипт. Я бы, вероятно, посмотрел, чтобы уменьшить его до ipaddresses=$(netstat -ano | awk '...'), так что вместо 3 grep процессов, плюс один cut и sed в строке, был всего один процесс 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 -c и sort -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.

Если ничего другого, это означает, что вы больше не создаете конвейер, который создает новые процессы cut и sed для каждой строки ввода, обработанной в первом цикле.

+0

Возможно, я не понимаю вас здесь, но насколько я могу рассказать о конвейере, пока метод на самом деле не работает? Это просто для рефакторинга или если он действительно работает так, как есть? – jett

+1

@jett Переадресовываете ли вы в оболочку 'while' оболочки Unix, используя канал - например,' '' - или другое перенаправление потока, такое как '<', не имеет никакого значения. Это особенность Unix-оболочек, каналов и стандартных входов, выходов и ошибок. – Ned

+1

@jett Что касается любой производительности с «подоболочками» в вашем скрипте, единственное, что вы можете сделать, это использовать каналы, а не создавать новые процессы 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 

Как вы можете видеть, почти нет внешних инструментов, используемых (не СЕПГ, AWK или GREP). Потрясающие!

Смежные вопросы