Bashスクリプトでgrepの使用率を向上させる

Bashスクリプトでgrepの使用率を向上させる

私は現在私のプログラムの1つで大容量ログファイルを処理するbashスクリプトを作成しています。初めて起動したときにスクリプトが完成するのに15秒ほどかかりましたが、悪くないレベルなのに改善したいと思います。キューを実装しmkfifo、解析時間を6秒に短縮しました。スクリプトの解析速度を向上させる方法があるかどうかを尋ねたいと思います。

現在のスクリプトバージョン:

#!/usr/bin/env bash
# $1 is server log file
# $2 is client logs file directory

declare -A orders_array

fifo=$HOME/.fifoDate-$$
mkfifo $fifo
# Queue for time conversion
exec 5> >(exec stdbuf -o0 date -f - +%s%3N >$fifo)
exec 6< $fifo
# Queue for ID extraction
exec 7> >(exec stdbuf -o0 grep -oP '[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*' >$fifo)
exec 8< $fifo
rm $fifo

while read line; do
    order_id=${line:52:36};
    echo >&5 "${line:1:26}"
    read -t 1 -u6 converted_time
    orders_array[$order_id]=$converted_time
done < <(grep -ah 'MarketOrderTransitions.*\[MarketMessages::OrderExecuted\]' $1)

while read line; do
    echo >&7 "$line"
    read -t 1 -u8 id
    echo >&5 "${line:1:26}"
    read -t 1 -u6 converted_time
    time_diff="$(($converted_time - orders_array[$id]))"
    echo "$id -> $time_diff ms"
done < <(grep -ah 'Event received (OrderExecuted)' $2/*market*.log)

このスクリプトの基本的な作業は、クライアントとサーバーのログファイルからメッセージのタイムスタンプを抽出し、一致するメッセージIDを見つけて、メッセージを送信するサーバーとメッセージを受信するクライアントの間の経過時間(ミリ秒)を計算することです。

最初のwhileループはかなり速く(1.5秒)完了しますが、2番目の部分(私の推測ではgrep)は時間がかかります。

テスト中のエンジンファイルの長さは約500,000行です。また、約700個のクライアントログファイル(合計130万行)があります。

注文IDはサーバーファイルの固定場所にありますが、クライアントログでそれを見つけるにはgrepする必要があります。

編集する:

提案どおりに入力ファイルの例を追加します。サーバー:

[2022-12-07 07:36:18.209496] [MarketOrderTransitionsa4ec2abf-059f-4452-b503-ae58da2ce1ff] [info] [log_process_event] [MarketMessages::OrderExecuted]
[2022-12-07 07:36:18.209558] [MarketOrderTransitionsa4ec2abf-059f-4452-b503-ae58da2ce1ff] [info] [log_guard] [[True] (lambda at ../subprojects/market_session/private_include/MarketSession/MarketOrderTransitions.hpp:81:24)]
[2022-12-07 07:36:18.209564] [MarketOrderTransitionsa4ec2abf-059f-4452-b503-ae58da2ce1ff] [info] [log_state_change] [GatewayCommon::States::New --> GatewayCommon::States::Executed]
[2022-12-07 07:36:18.209567] [MarketOrderTransitionsa4ec2abf-059f-4452-b503-ae58da2ce1ff] [info] [log_action] [(lambda at ../subprojects/market_session/private_include/MarketSession/MarketOrderTransitions.hpp:57:25) for event: MarketMessages::OrderExecuted]
[2022-12-07 07:36:18.209574] [MarketOrderTransitionsa4ec2abf-059f-4452-b503-ae58da2ce1ff] [info] [log_process_event] [boost::sml::v1_1_0::back::on_entry<boost::sml::v1_1_0::back::_, MarketMessages::OrderExecuted>]

IDはMarketOrderTransitions(a4ec2abf-059f-4452-b503-ae58da2ce1ff)の後の角かっこ内にあります。

顧客

[2022-12-07 07:38:47.545433] [twap_algohawk] [info] [] [Event received (OrderExecuted): {"MessageType":"MarketMessages::OrderExecuted","averagePrice":"49.900000","counterPartyIds":{"activeId":"dIh5wYd/S4ChqMQSKMxEgQ**","executionId":"2295","inactiveId":"","orderId":"3dOKjIoURqm8JjWERtInkw**"},"cumulativeQuantity":"1200.000000","executedPrice":"49.900000","executedQuantity":"1200.000000","executionStatus":"Executed","instrument":[["Symbol","5"],["Isin","5"],["SecurityIDSource","4"],["Mic","MARS"]],"lastFillMarket":"MARS","leavesQuantity":"0.000000","marketSendTime":"07:38:31.972000000","orderId":"a4ec2abf-059f-4452-b503-ae58da2ce1ff","orderPrice":"49.900000","orderQuantity":"1200.000000","propagationData":[],"reportId":"Qx2k73f7QqCqcT0LTEJIXQ**","side":"Buy","sideDetails":"Unknown","transactionTime":"00:00:00.000000000"}]

クライアントログのIDはorderIdタグ内にあります(2つあり、私は2番目を使用します)。

希望の出力は次のとおりです。

98ddcfca-d838-4e49-8f10-b9f780a27470 -> 854 ms
5a266ca4-67c6-4482-9068-788a3520b2f3 -> 18 ms
2e8d28de-eac0-4776-85ab-c75d9719b7c6 -> 58950 ms
409034eb-4e55-4e39-901a-eba770d497c0 -> 56172 ms
5b1dc7e8-fae0-43d2-86ea-d3df4dbe810b -> 52505 ms
5249ac24-39d2-40f5-8adf-dcf0410aebb5 -> 17446 ms
bef18cb3-8cef-4d8a-b244-47fed82f21ea -> 1691 ms
7c53c950-23fd-497e-a011-c07363d5fe02 -> 18194 ms

特に、ログファイルの「注文の実行」メッセージが心配です。

答え1

現在の場所を示すために、クライアントとサーバーという2つの入力ファイルを表示し、各ファイルでIDを見つけることができる場所を示します。次のようにawkを使用します。

$ cat tst.sh
#!/usr/bin/env bash

awk '
    (NR == FNR) && match($0,/\[MarketOrderTransitions[^]]+]/) {
        id = substr($0,RSTART+23,RLENGTH-24)
        print FILENAME, id
    }
    (NR > FNR) && match($0,/.*"orderId":"/) {
        id = substr($0,RLENGTH+1)
        sub(/".*/,"",id)
        print FILENAME, id
    }
' "$@"

$ ./tst.sh Server Client
Server a4ec2abf-059f-4452-b503-ae58da2ce1ff
Server a4ec2abf-059f-4452-b503-ae58da2ce1ff
Server a4ec2abf-059f-4452-b503-ae58da2ce1ff
Server a4ec2abf-059f-4452-b503-ae58da2ce1ff
Server a4ec2abf-059f-4452-b503-ae58da2ce1ff
Client a4ec2abf-059f-4452-b503-ae58da2ce1ff

また、予想される出力は、横に数字がある類似した形のIDのリストであると述べましたが、これらのIDは、提供された入力例とは無関係のようで、数字がどこから来たのかを教えてくれませんでした。

あなたの要件を表現し、質問にテスト可能な例を提供できるようになったら、このスクリプトを完成させることができます。これはあなたのシェルスクリプトよりもはるかに速く実行されます。

あなたがやろうとしていることの1つの推測は次のとおりです。 GNU awkを使用して時間機能を実行することです。

$ cat tst.sh
#!/usr/bin/env bash

awk '
    { time = substr($0,2,26) }
    (NR == FNR) && match($0,/\[MarketOrderTransitions[^]]+]/) {
        id = substr($0,RSTART+23,RLENGTH-24)
        orders_time[id] = time
    }
    (NR > FNR) && match($0,/.*"orderId":"/) {
        id = substr($0,RLENGTH+1)
        sub(/".*/,"",id)
        time_diff = time2ms(time) - time2ms(orders_time[id])
        print id " -> " time_diff " ms"
    }

    function time2ms(time,      t,secs) {
        gsub(/[-:]/," ",time)
        split(time,t,/[.]/)
        return ( mktime(t[1]) substr(t[2],1,3) )
    }
' "$@"

$ ./tst.sh Server Client
a4ec2abf-059f-4452-b503-ae58da2ce1ff -> 149336 ms

しかし、あなたが投稿した予想出力は、あなたが投稿したサンプル入力とは関係がないと思われるので、これが正しいかどうかはわかりません。

関連情報