배열 구축

배열 구축

저는 현재 하나(또는 여러 개)의 서버 로그에서 관련 행을 모두 읽고 순서대로 표시하는 스크립트를 작성하려고 합니다. 쉘 스크립팅을 처음 접해서 실수를 많이 했는데, 지금은 스크립트가 너무 느려서 제대로 작동하지 않습니다.

이것이 내 문제입니다. 먼저 오류 코드로 일부 레코드를 식별합니다. 해당 오류 코드가 포함된 줄은 구조화되지 않은 xml입니다. 이 오류와 관련된 다른 행을 가져오려면 XML에서 ID가 포함된 다른 태그를 찾아야 합니다. 이 ID는 타임스탬프 및 관련 스레드 또는 작업 번호가 있는 다른 행과 일치합니다. 이 두 값을 사용하면 이론적으로 오류 메시지에 "가까운" 스레드 번호가 있는 모든 행을 찾아서 오류와 관련된 모든 행을 얻을 수 있습니다. 이론적으로 파일 크기는 150MB이기 때문입니다.

그래서 다음과 같이 코드를 작성했습니다.

grep -c오류 수를 가져오고 grep해당 오류가 포함된 각 줄을 를 사용하여 파일에 sed쓰고, 파일을 반복하여 ID를 찾아 배열에 씁니다.

그런 다음 forID를 반복 하여

  • grep초기 파일을 사용하여 ID, 작업 및 타임스탬프가 포함된 줄 찾기
  • 작업 및 타임라인을 변수에 저장
  • 그런 다음 원본 파일을 다시 반복하여 내 작업이 포함된 각 줄과 내 참조에 가까운 타임스탬프(초 단위)를 찾습니다.

...여기서는 속도가 느려져서 죽습니다.

seds 루프에서 s를 수행하는 것이 grep이상적이지는 않지만 특정 지점에서 파일을 읽는 도구를 찾지 못했습니다(예: xy 행 등을 사용).

해당 줄이 내 오류 코드가 포함된 줄이나 내 ID 및 작업이 포함된 줄로 시작하지 않고, 거기에 관련되지 않은 줄이 여러 개 있을 수 있으므로 grep첫 번째 줄을 알고 있는 . 그리고 텍스트의 마지막 줄에도 불구하고 그것을 유리하게 사용할 방법을 찾지 못했습니다.

어떤 도움이라도 대단히 감사하겠습니다. 감사합니다.

편집: 네, 죄송합니다. 조금 모호합니다. 이제 외부 프로그램 호출 횟수를 줄여 성능이 크게 향상되었습니다. 내가 이전에 한 일은 다음과 같습니다.

while read Buffer; do

  TimeStamp=$(echo $Buffer | sed 's/blabla(Timestamp)blabla/\1/g')
  [ TimeStamp -ge CompareStamp ] || continue
  echo $Buffer >>./mylog
done

그래서 저는 각 행을 살펴보고 해당 행의 타임스탬프가 제가 저장한 타임스탬프와 가까운지 확인하고 있습니다. 너무 느립니다. 해당 코드를 라인의 타임스탬프 부분만 비교하고 참조 전후의 두 번째 또는 두 번째에 맞는지 확인하는 3개의 grep으로 대체했습니다. 이것은 작동하지만 정말 추악합니다. 또한 서버가 3초 이내에 이 작업의 여러 사례를 처리할 수 있기 때문에 참조용 행만 찾는다고 보장할 수 없습니다.

내 로그는 다음과 같습니다.

timestamp first entry task blabla
timestamp blabla task blabla
timestamp blabla task blabla
timestamp blabla task reference
timestamp blabla task blabla
timestamp blabla task blabla
<xml><error>error</error></error>
timestamp blabla task blabla
timestamp last entry task blabla

마지막 항목과 첫 번째 항목이 무엇인지 알고 있으므로 검색할 수 있습니다. 동일한 작업이 포함된 블록은 로그 파일에서 해당 블록 전후에 반복되며, 다른 작업에 대한 줄도 해당 블록 내에 있을 수 있습니다. 그래서 첫 번째 단계는 해당 작업이 포함된 모든 줄을 별도의 파일에 넣어 더 적은 데이터를 수집하고 다른 작업에 대해 걱정하지 않는 것이었습니다.

따라서 일반 프로그래밍에서는 이제 한 줄씩 읽고 그것이 첫 번째 항목인지 확인하고 항상 마지막 첫 번째 항목의 위치를 ​​저장한 다음 참조를 찾은 후 위치 저장으로 돌아가서 각 항목 라인을 읽습니다. 마지막 항목을 찾을 때까지. 스크립트를 다시 인간의 속도로 늦추지 않고 쉘을 사용하여 이 효과를 얻을 수 있는 방법이 있습니까?

Edit2: 좋습니다. 대부분은 다음과 같습니다. 방금 정규식과 검색 문자열을 제거했습니다.

grep 'Errorcode' logfile >> ./grablog
NumOfErrors=$(grep -c 'Errorcode' grablog)

AllPrimaryReferences=($(sed -r 's/^.*(<Referencetag>)([^<]*)(<\/Referencetag>).*$/\2/g' grablog))  

j=0
for ((i=0;i<NumOfErrors;i=i+2))
do

    Reference=$(grep  'blablablabla = '${AllPrimaryRefernces[i]} logfile)
    TimeStamp=$(echo "$Reference" | sed -r 's/^ganze Zeile/timestamp/g')

    AllTasks[j]=$(echo "$Reference" | sed -r 's/ganze Zeile/Reference/g')

    grep "${AllTasks[j]}" logfile >>./tempfile


    CompTimeStamp=$(date -d "$TimeStamp" +%Y-%m-%d' '%X)
    grep 'CompTimeStamp' tempfile >>./output


    rm tempfile
    let j++
done

rm grablog
´´´

답변1

스크립트에는 낭비처럼 보이고 효율성이라는 이름으로 재배치될 수 있는 일부 부분이 있습니다.

배열 구축

grep 'Errorcode' logfile >> ./grablog
NumOfErrors=$(grep -c 'Errorcode' grablog)

AllPrimaryReferences=($(sed -r 's/^.*(<Referencetag>)([^<]*)(<\/Referencetag>).*$/\2/g' grablog))  

나중에 "NumOfErrors"를 종료 조건으로 사용하고 +=2while 루프를 사용하여 배열을 반복합니다. 를 통해 직접 배열의 길이에 액세스할 수 있습니다 ${#arrayname[@]}.

즉, 한 번만 읽어야 하거나 grablog파이프에서 읽어야 하므로 임시 파일이 제거됩니다.

AllPrimaryReferences=( $(grep 'Errorcode' logfile | sed -r '...' | uniq ) )
NumOfRefs=${#AllPrimaryReferences[@]}

uniq중복 항목을 +=2건너뛰기 위해 사용하는 대신 제거 해야 합니다 . 나는 그들이 항상 서로 쌍을 이루고 있다고 가정하고 있습니다(로그 파일을 볼 수 없기 때문에 확인할 수 없습니다). 그렇지 않은 경우 sort이전에 추가 할 수 있지만 uniq항목이 많으면 속도가 느려집니다.

그러나 루프 내부의 스크립트 부분은 더 많은 주의를 기울여야 하는 부분입니다.

변수 추출sed

sed각 루프를 두 번 실행하면 속도가 느려질까 봐 걱정됩니다 . 이를 방지할 수 있는 몇 가지 방법은 다음과 같습니다.

둘이 하나로sed

sed하나의 명령으로 전체 라인을 배열로 연결하여 두 개의 변수를 추출 할 수 있습니까 ?

ts_task=( $(echo "$Reference" | sed 's/\(timestamp_regex\).*\(task_regex\)/\1 \2/' ) )
timestamp="${ts_task[0]}"
task="${ts_task[1]}"

bash대안 사용

sed어쩌면 전혀 필요하지 않고 $References필요한 부분 문자열을 추출할 수도 있습니다.매개변수 확장. 예를 들어. 첫 번째 공백 이전의 모든 내용을 추출합니다.

timestamp=${References%% *}

명명된 파이프 사용

걱정된다면시작 sed천천히 해라, 너할 수 있다루프가 시작되기 전에 백그라운드에서 시작하고 명명된 파이프를 사용하여 메인 루프와 통신합니다. 읽기/쓰기가 중단된 프로세스를 기억하고 이를 백그라운드에 두거나 적절하게 기다려야 하기 때문에 이는 지루할 수 있습니다.

전체 입력 파일을 두 번 읽고 매번 한 번씩 반복합니다.

이것은 아마도 가장 느린 부분이고 개선 가능성이 가장 큰 부분일 것입니다.

참조에서 세부정보 추출

귀하의 의견에서 인용문당 한 줄만 있다고 언급하셨습니다 blablabla. 이는 첫 번째 라운드가 grep전체 입력 파일을 보고 일치하는 항목에서 중지한 다음 다음 라운드가 처음부터 시작하여 다음 항목을 찾는다는 것을 의미합니다.

참조당 이러한 라인이 하나만 있는 경우 전체 "배열 구축" 단계는 아마도 불필요하며 루프를 직접 입력할 수 있습니다.

grep 'blablablabla = ' logfile | # match each line that defines a primary reference
  sed '...' |                    # command to extract just the timestamp and taskname
  while read ts task ; do        # assign the two required variables

    # use ts and task to extract everything as before

  done

이는 grep두 개의 s 중 첫 번째가 이제 루프 외부에 있으므로 한 번만 실행된다는 것을 의미합니다.

결과 필터링

행의 하위 집합을 덤프 tempfile한 다음 grep잠시 동안 결과에 대해 실행합니다. 이전과 마찬가지로 이 두 단계를 파이프라인으로 결합하면 임시 파일 사용을 피할 수 있습니다.

grep "$task" logfile | grep "$timestamp" >> output

또는 전체 줄의 형식에 대해 충분히 알고 있는 경우

grep "$timestamp <match other part of line> $task" logfile >> output

전체 알고리즘

이러한 모든 개선 사항에도 불구하고 전체 로그 파일을 다시 읽고 모든 참조/작업에 대해 해당 파일의 모든 줄을 다시 확인하는 병목 현상이 발생할 수 있습니다. 이는 필요한 줄이 로그 파일의 어느 위치에나 어떤 순서로든 나타날 수 있는 경우에 적합합니다. 이는 모든 줄을 찾는 무차별 대입 방법이므로 시간이 오래 걸립니다.

근데 너가 갖고 있잖아힌트구조와 컨텍스트("첫 번째 및 마지막 행", "첫 번째 항목")를 사용하면 더 스마트한 접근 방식이 가능합니다. 입력 파일의 구조/순서에 대해 더 많이 알고 있는 경우 작업 중복을 피하기 위해 추가 단축키를 사용할 수 있습니다.

'위치 저장' 및 '저장된 위치로 돌아가기' 방법을 문의하셨습니다. grep -n각 일치 항목의 줄 번호를 보고합니다.tail(1)이 명령은 파일 시작 부분에서 여러 줄을 건너뛸 수 있지만 줄 바꿈을 찾으려면 파일을 다시 읽어야 합니다. 전체 파일을 while read루프로 처리할 수 있을까요?

관련 정보