Otimizando um script de shell com longa duração enquanto loop

4

Eu escrevi um script de shell que tem que fazer o seguinte:

  1. Capture os comandos da sessão em um arquivo.
  2. Cada comando individual em um arquivo separado.
  3. Envie todo o conteúdo do arquivo de comando individualmente com base em determinados critérios.

Quanto à minha observação, o loop tem que iterar no mínimo 25.000 vezes. Agora, meu problema é que leva mais de 6 horas para concluir todas as iterações.

Abaixo está a parte principal do script que está demorando para ser processada.

 if [ -s "$LOC/check.txt" ]; then

    while read line; do
            echo -e " started processing $line at 'date' " >> "$SCRIPT_LOC/running_status.txt"
            TST='grep -w $line $PERM_LOC/id_processing.txt'
            USER='echo $TST | grep -w $line | awk -F '"' '{print $10}''
            HOST='echo $TST | grep -w $line | awk -F '"' '{print $18}''
            ID='echo $TST | echo $line | tr -d '\"''
            IP='echo $TST | grep -w $line | awk -F '"' '{print $20}''
            DB='echo $TST | grep -w $line | awk -F '"' '{print $22}''
            CONN_TSMP='echo $TST | grep -w $line | awk -F '"' '{print $2}''

            if [ -z "$IP" ]; then
                    IP='echo "$HOST"'
            fi

            if [ "$USER" == "root" ] && [ -z $DB ]; then
                     TARGET=/data1/sessions/root_sec
                     CMD_TARGET=/data1/commands/root_commands
                     FILE='echo "$ID-$CONN_TSMP-$USER@$IP.txt"'
            else
                     TARGET=/data1/sessions/user_sec
                     CMD_TARGET=/data1/commands/user_commands
                     FILE='echo "$ID-$CONN_TSMP-$USER@$IP.txt"'
            fi

            ls $TARGET/$FILE
            If [ $? -ne 0 ]; then
                     echo $TST | awk -F 'STATUS="0"' '{print $2}'| sed "s/[</>]//g" >> "$TARGET/$FILE"
                     echo -e "\n" >> "$TARGET/$FILE"
             fi

             grep $line  $LOC/out.txt  > "$LOC/temp.txt"

             while read val; do
                      TSMP='echo "$val" | awk -F '"' '{print $2}''
                      QUERY='echo "$val" | awk -F 'SQLTEXT=' '{print $2}' | sed "s/[/]//g"'
                       echo " TIMESTAMP=$TSMP " >> "$TARGET/$FILE"
                       echo " QUERY=$QUERY " >> "$TARGET/$FILE"
                       RES='echo "$QUERY" | awk {'print $1'} | sed 's/["]//g' '
                       TEXT='grep "$RES" "$PERM_LOC/commands.txt"'
                       if [ -n "$TEXT" ]; then
                               NUM='expr $NUM + 1'
                               SUB_FILE='echo "$ID-$command-$NUM-$TSMP-$USER@$IP.txt"'
                               echo -e "===============\n" > "$CMD_TARGET/$SUB_FILE"
                               echo "FILE      =   \"$SUB_FILE\"" >> "$CMD_TARGET/$SUB_FILE"
                                ### same way append 6 more lines to $SUB_FILE            

                                SUB='echo "$WARN_ME" | grep "$command"'
                                if [ "$command" == "$VC" ]; then
                                      STATE='echo " very critical "'
                                elif [ -z "$SUB" ]; then
                                      STATE=CRITICAL
                                else
                                       STATE=WARNING
                                fi

                                if [ "$USER" != "root" -a "$command" != "$VC" ]; then
                                       mail command &
                                elif [ "$USER" == "root" -a -z "$HOST" ]; then
                                       mail command &
                                elif [ "$USER" == "root" -a "$command" == "$VC" ]; then
                                       mail command &
                                else
                                       echo -e "some message \n" >> $LOC/operations.txt
                                fi
                       fi
             done < "$LOC/temp.txt"
    done < "$LOC/check.txt"
 fi

Alguém pode me ajudar a otimizar esse código dividindo ou alterando a lógica ou usando funções ou qualquer outra coisa?

Aqui eu tenho que usar apenas um script de shell e o servidor no qual o script será executado não deve levar mais que 3GB de RAM para processá-lo.

Qualquer ajuda é muito útil.

    
por vidyadhar 17.08.2013 / 14:19

2 respostas

4

Oh meu Deus!

Eu posso ver por que demora uma eternidade para executar, você está repetindo as operações, não armazenando informações em cache e praticamente vencendo o computador até a morte. Computador Pobre. : (

O awk não é leve e você o invoca muitas e muitas vezes nos mesmos dados. Consegui executá-lo uma vez e definir todas as cinco variáveis.

Sem saber o que isso deveria estar fazendo ou realizando, há tanta coisa que pode ser feita.

Considerando que TODO o processamento é grep, awk, sed e tr, você pode obter um impressionante aumento de velocidade escrevendo este script em PERL. PERL é / foi projetado para lidar com texto e relatórios. Ele pode fazer todos os grep / awk / sed / tr internamente sem gastar muito para outro programa repetidamente.

Mas aqui estão algumas melhorias:

if [ -s "$LOC/check.txt" ]; then

function setvars() {
    CONN_TSMP="$1"
    USER="$2"
    HOST="$3"
    DB="$4"
    IP="$5"
    return
}
    while read line; do
        echo " started processing ${line} at $(date) " >> "${SCRIPT_LOC}/running_status.txt"
        ID=$(echo "$line" | tr -d '"')
        # are you sure you don't want the FIRST match?  This will give ALL the matches,
        # which will prevent you from getting good values for the variables
        # to only get first entry that matches:
        # TST=$(grep --max-count=1 -w "$line" "$PERM_LOC/id_processing.txt")
        # (or -m 1, but long options document what you're doing better)
        TST=$(grep -w "$line" "$PERM_LOC/id_processing.txt")
        VARS=$(echo "${TST}" | awk -F '"' '{print "\""$2"\" \""$10"\" \""$18"\" \""$20"\" \""$22'})
        #                                        CONN_TSMP     USER      HOST      IP        DB
        # magic!  setvars receives the 5 values awk pulled out (ran it once!)
        # NO QUOTES on next line, already has them embedded from awk
        setvars $VARS

        if [ -z "$IP" ]; then
            IP="$HOST"
        fi

        CMD_TARGET="/data1/commands/user_commands"
        FILE="${ID}-${CONN_TSMP}-${USER}@${IP}.txt"

        if [ "$USER" == "root" ] && [ -z "$DB" ]; then
            TARGET="/data1/sessions/root_sec"
        else
            TARGET="/data1/sessions/user_sec"
        fi

        # does this need to be redirected to a file?
        ls "$TARGET/$FILE"
        if [ $? -ne 0 ]; then
            # awk can likely do the print and the removal of </> characters in
            # one pass (my awk-fu is weak this morning)
            echo "$TST" | awk -F 'STATUS="0"' '{print $2}'| sed "s/[</>]//g" >> "$TARGET/$FILE"
            echo -e "\n" >> "$TARGET/$FILE"
        fi

        # ALWAYS quote your values, embedded spaces will bite you!
        grep "$line" "$LOC/out.txt" > "$LOC/temp.txt"

        while read val; do
            TSMP=$(echo "$val" | awk -F '"' '{print $2}')
            QUERY=$(echo "$val" | awk -F 'SQLTEXT=' '{print $2}' | sed "s/[\"/]//g")
            echo " TIMESTAMP=$TSMP " >> "$TARGET/$FILE"
            echo " QUERY=$QUERY " >> "$TARGET/$FILE"
            TEXT=$(grep "$QUERY" "$PERM_LOC/commands.txt")
            if [ -n "$TEXT" ]; then
                NUM=$(expr $NUM + 1)
                # could also be:  NUM=$(($NUM+1)) (bash v4.0+)
                SUB_FILE="$ID-$command-$NUM-$TSMP-$USER@$IP.txt"
                echo -e "===============\n" > "$CMD_TARGET/$SUB_FILE"
                echo "FILE      =   \"$SUB_FILE\"" >> "$CMD_TARGET/$SUB_FILE"
                ### same way append 6 more lines to $SUB_FILE

                SUB=$(echo "$WARN_ME" | grep "$command")
                if [ "$command" == "$VC" ]; then
                    STATE=" very critical "
                elif [ -z "$SUB" ]; then
                    STATE=" CRITICAL "
                else
                    STATE=" WARNING "
                fi

                if [ "$USER" != "root" -a "$command" != "$VC" ]; then
                    # this should probably be $command instead of command?
                    # oh wait, probably a placeholder statement
                    mail command &
                elif [ "$USER" == "root" -a -z "$HOST" ]; then
                    mail command &
                elif [ "$USER" == "root" -a "$command" == "$VC" ]; then
                    mail command &
                else
                    echo -e "some message \n" >> $LOC/operations.txt
                fi
            fi
        done < "$LOC/temp.txt"
    done < "$LOC/check.txt"
fi

Hmm, "apenas script de shell". Bem, com isso em mente, talvez você possa pre-grep "$ LOC / check.txt" e / ou "$ LOC / temp.txt" para que você possa usar a saída "já grepped" em vez de usar o loop.

Quanto mais eu olho para isso, mais eu estou convencido de que o awk provavelmente faria todo esse trabalho em uma única passagem pelos dados ... E processaria CADA entrada, não apenas a primeira (como eu indiquei no comentários, você realmente precisa de outro loop entre os loops "ler linha" e "ler var".)

Seria um roteiro longo, mas definitivamente factível. E awk vale a pena conhecer, tire um momento e brinque com isso, não é tão difícil, apenas diferente. Grok Awk!

    
por 17.08.2013 / 15:17
0

Você postou um código que não é executado e está sem muitas informações vitais. Sua informação adicional realmente não esclarece exatamente o que é sua entrada e sua saída desejada.

No entanto, aqui está a minha opinião sobre a remoção de todas as chamadas sed e awk do seu script e simplificá-lo significativamente para poder depurar seus problemas de desempenho corretamente:

#!/usr/bin/env bash
# Should work using bash 3.2+ and the unrevealed part of your code

if [ ! -s "$LOC/check.txt" ]; then
    echo "Bummer!"
    exit 1
fi

function write_ts () {
    echo "[$(date)]: Started processing ${line}" >> ${SCRIPT_LOC}/running_status.txt
}

function set_and_init_file_targets () {
    if [ "$USER" == "root" ] && [ -z $DB ]; then
        TARGET=/data1/sessions/root_sec
        CMD_TARGET=/data1/commands/root_commands
    else
        TARGET=/data1/sessions/user_sec
        CMD_TARGET=/data1/commands/user_commands
    fi
    FILE="${CONNECTION_ID}-${TIMESTAMP}-${USER}@${IP}.txt"

    if [ ! -e "$TARGET/$FILE" ]; then
        echo "${_res##*STATUS=0}" > "$TARGET/$FILE"
    fi
}

function parse_line () {
    local line=$@

    while read val; do
        res2=${val//[<>\(\)]/}
        eval ${res2//AUDIT_RECORD/}
        SQLTEXT=${SQLTEXT/%?/}
        echo "TIMESTAMP=$TIMESTAMP" >> "$TARGET/$FILE"
        echo "QUERY=$SQLTEXT" >> "$TARGET/$FILE"

        /* grep the sql command by itself */
        TEXT=$(grep -i "${SQLTEXT%% *}" "$PERM_LOC/commands.txt")
        if [ -n "$TEXT" ]; then
            NUM=$((NUM + 1))
            SUB_FILE="$CONNECTION_ID-$command-$NUM-$TIMESTAMP-$USER@$IP.txt"
            echo -e "===============\n" > "$CMD_TARGET/$SUB_FILE"
            echo "FILE      =   \"$SUB_FILE\"" >> "$CMD_TARGET/$SUB_FILE"

            # [... the rest does not make sense at all ...]

        fi
    done < <(grep "$line" "$LOC/out.txt")
}

# Main code
while read line; do
    # grep line without quotes
    TST=$(grep -w "${line//\"/}" "$PERM_LOC/id_processing.txt")
    # remove everything besides key=val pairs
    res=${TST//[<>\(\)]/}
    # set the key=val pairs, except AUDIT_RECORD
    eval ${res//AUDIT_RECORD/}
    # set IP to HOST if empty
    : ${IP:="$HOST"}
    # remove nasty / at the end
    DB=${DB/%?/}
    set_and_init_file_targets
    parse_line "$line"
done < "$LOC/check.txt"

Depois de postar isso, não estou realmente convencido de que seus problemas de desempenho derivem apenas desses dois loops forçando o awk / sed / grep. Você poderia produzir as primeiras dez linhas do seu ${SCRIPT_LOC}/running_status.txt quando seu script é executado por uma hora ou mais?

Observe que meus fragmentos de script não foram testados e podem não funcionar de acordo com suas expectativas. No entanto, tentei seguir a semântica do trecho do script inicial.

    
por 24.08.2013 / 21:30