Bash 스크립트에서 grep 사용 속도 향상

Bash 스크립트에서 grep 사용 속도 향상

나는 현재 내 프로그램 중 하나에서 대용량 로그 파일을 처리하는 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초) 완료되지만 두 번째 부분(내 추측으로는 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 태그 안에 있습니다(두 개가 있는데 저는 두 번째를 사용합니다).

원하는 출력은 다음과 같습니다.

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는 제공한 예제 입력과 관련이 없는 것 같고 숫자가 어디서 왔는지 알려주지 않았습니다.

귀하의 요구 사항을 표현하고 질문에 테스트 가능한 예를 제공할 수 있게 되면 이 스크립트를 완성할 수 있으며 이는 귀하의 셸 스크립트보다 훨씬 빠르게 실행될 것입니다.

당신이 하려는 일에 대한 한 가지 추측은 다음과 같습니다. 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

하지만 귀하가 게시한 예상 출력은 귀하가 게시한 예제 입력과 관련이 없는 것 같아서 이것이 맞는지 모르겠습니다.

관련 정보