長期実行whileループを使用したシェルスクリプトの最適化

長期実行whileループを使用したシェルスクリプトの最適化

私は以下を行う必要があるシェルスクリプトを書いています。

  1. セッションコマンドをファイルとしてキャプチャします。
  2. 各個別コマンドは別々のファイルに保存されます。
  3. 特定の基準に従って、個々のコマンドファイルの内容をメールで送信します。

私が観察したところ、ループは少なくとも25,000回繰り返す必要があります。今私の問題は、すべての繰り返しを完了するのに6時間以上かかることです。

以下は、処理に長い時間がかかるスクリプトの主要部分です。

 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

誰もがロジックを分割、変更したり、関数や他のものを使用してこのコードを最適化する方法を助けることができますか?

ここではシェルスクリプトのみを使用する必要があり、スクリプトを実行しているサーバーはそれを処理するために3 GB以上のRAMを占有しないでください。

どんな助けでも非常に役立ちます。

ベストアンサー1

マブソサ!

実行に時間がかかる理由を理解しています。情報をキャッシュしてコンピュータをほとんど殺すのではなく、操作を繰り返しています。貧しいコンピュータ。 :(

awkは軽くないので、同じデータに対して複数回呼び出します。一度実行すると、5つの変数をすべて設定できます。

これが何をすべきか、何を成し遂げるべきかを知らないままできることが多すぎます。

すべての処理がgrep、awk、sed、およびtrであることを考慮すると、このスクリプトをPERLで作成すると、印象的なスピードアップが得られます。 PERLは、テキストとレポートを処理するように設計されています。他のプログラムを繰り返し呼び出すことなく、内部的にこれらのすべてのgrep / awk / sed / tr操作を実行できます。

しかし、いくつかの改善点は次のとおりです。

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

まあ、「シェルスクリプトのみ」です。さて、これを念頭に置いて、「$LOC/check.txt」および/または「$LOC/temp.txt」をあらかじめgrepして、ループの代わりに「grep」出力を使用できます。 。

もっと見るほど、awkは一度にデータを処理することでこれを行うことができ、最初の項目だけでなく各項目を処理できるという確信が高まりました(コメントで指摘したように、別のループが必要です)。 「read line」と「read var」ループの間)。

これは長いawkスクリプトになりますが、確かに実行可能です。そしてawkは時間をかけて学ぶ価値があり、それほど難しくなく、ただ違うだけです。マブソサ!

おすすめ記事