파일 끝에 도달할 때까지 데이터 블록을 다시 포맷하는 방법은 무엇입니까?

파일 끝에 도달할 때까지 데이터 블록을 다시 포맷하는 방법은 무엇입니까?

다음과 같은 파일이 있습니다.

# Time-averaged data for fix avetimeall
# TimeStep Number-of-rows
# Row c_gyrationchunkall
1000 3
1 2.09024e-14
2 4.88628
3 5.69321
2000 3
1 2.10518e-14
2 8.33702
3 8.83162
3000 3
1 1.96656e-14
2 12.1396
3 11.5835
...

내 파일에서 처음 세 줄은 항상 헤더입니다. 헤더 뒤에는 내 파일에 동일한 크기의 청크가 나열되어 있으며 각각은 태그 하위 헤더로 시작됩니다. 각 청크의 데이터가 해당 청크 레이블의 관련 부분으로 시작하는 줄로 전송되도록 파일의 데이터를 재구성한 다음 해당 청크에 대한 관련 데이터 값을 모두 공백으로 구분하여 나열하고 싶습니다. 예를 들어, 위의 예를 다음과 같이 변환하고 싶습니다.

# Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835
...

Bash에서 이 작업을 어떻게 수행하나요? Bash 경험이 좀 있지만 이 문제를 신속하게 처리하기에는 충분하지 않습니다.

답변1

3블록의 줄 수가 달라질 수 있는지 여부 에 관계없이 awk를 사용하십시오 .

$ awk '
    NR == 2 { $3=""; saved=$0; next }
    NR == 3 { $0=saved $3 }
    NR  < 4 { print; next }
    !numLines {
        numLines = $2
        printf "%s%s", $1, OFS
        next
    }
    { printf "%s%s", $2, (--numLines ? OFS : ORS) }
' file
# Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

아래 토론을 계속 진행하세요.자비에르 G의 답변가독성 스타일 기본 설정과 관련하여 다음은 쉘 스크립트와 동일한 스타일로 작성된 awk 스크립트입니다(쉘 스크립트 내에 포함되어 있어 외부에서도 동일한 방식으로 작동함). 그러나 훨씬 더 빠르게 실행됩니다* 쉘 스크립트보다 더 강력하고 이식 가능합니다. :

$ cat ./script_filename
#!/usr/bin/env bash

awk '
    BEGIN {
        # Reformat comments:
        getline first_line
        print first_line
        getline; split($0,line2)
        getline; split($0,line3)
        printf "# %s %s\n", line2[2], line3[3]

        # Reformat data:
        while ( getline > 0 ) {
            timestep=$1; number_of_rows=$2
            printf "%s", timestep
            for ( i=1; i<=number_of_rows; i++ ) {
                getline; row_value=$NF
                printf " %s", row_value
            }
            print ""
        }
    }
'

$ ./script_filename < input
# Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

* 다음은 90,000개의 OP 레코드가 포함된 파일에서 bash 스크립트와 위의 awk 스크립트를 세 번째로 실행한 타이밍 결과입니다.

$ time ./script_bash < file > /dev/null

real    0m9.425s
user    0m5.062s
sys     0m4.139s

$ time ./script_awk < file > /dev/null

real    0m0.265s
user    0m0.171s
sys     0m0.000s

답변2

사용행복하다(이전 Perl_6)

skip머리글 행을 일시적으로 잊어버리는 데 사용됩니다 .

~$ raku -e 'my @a = lines.skip(3).rotor(4, partial => True).map: *.words; .[0,3,5,7].put for @a;'  file

#OR

~$ raku -e 'my @a = lines.skip(3).batch(4).map: *.words; .[0,3,5,7].put for @a;'  file

위 내용은 Perl 계열의 프로그래밍 언어인 Raku로 작성된 답변입니다. 즉, 처음 3개의 헤더 라인을 lines읽고 ping합니다. 파일 끝에 있는 마지막 "스핀"을 포함하여 skip4줄마다 함께 rotor편집됩니다 . 그 동안 각 / 를 공백으로 구분해 보겠습니다 .batchpartialrotorbatchwords

이러한 로터/배치의 4개 행 각각은 여백에서 분리되어 @이라는 서명 배열 에 저장됩니다 @a. 마지막으로(두 번째 명령문에서) for각 위치를 사용하여 @a반복하고 put, 원하지 않는 요소를 제거하도록 주의합니다(색인 대괄호를 통해 [0,3,5,7]).

입력 예:

# Time-averaged data for fix avetimeall
# TimeStep Number-of-rows
# Row c_gyrationchunkall
1000 3
1 2.09024e-14
2 4.88628
3 5.69321
2000 3
1 2.10518e-14
2 8.33702
3 8.83162
3000 3
1 1.96656e-14
2 12.1396
3 11.5835

예제 출력:

1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

put헤더 라인과 관련하여 등의 두 가지 문으로 Raku 코드를 시작하는 것이 쉬울 수 있습니다 put "Time-averaged data...";. 그러나 실제로 다음은 OP에 원하는 출력을 제공하는 작업입니다.

~$ raku -e 'lines[0].put; .words[0..1, *-1].put for lines[0..1].rotor(2);  \
            my @a = lines.rotor(4, partial => True).map: *.words;          \
            .[0,3,5,7].put for @a;'  file
## Time-averaged data for fix avetimeall
# TimeStep c_gyrationchunkall
1000 2.09024e-14 4.88628 5.69321
2000 2.10518e-14 8.33702 8.83162
3000 1.96656e-14 12.1396 11.5835

https://raku.org

답변3

사용AWK:

$ awk '
    NR==2{sub(/[[:space:]]+[^[:space:]]+$/,"");rec = $0; next}
    NR==3{$0 = rec OFS $NF};
    NR<4;                                
    NR>3{printf "%s", (NR%4==0) ? ((NR==4) ? "" : ORS) $1 : ($1="")$0 }
   END{if (NR)print ""}' 
$ awk '
   NR==2{sub(/[[:space:]]+[^[:space:]]+$/,"");rec = $0; next}
   NR==3{$0 = rec OFS $NF};
   NR<4;
   $NF ~ /^[0-9]+$/{a=$NF;n=NR+a; sub(/[[:space:]]+[^[:space:]]+$/,""); printf "%s", $0; next}                    
   NR<=n{$1 =""; printf "%s", $0((NR==n) ? ORS : "") }'

답변4

질문에 언급된 경고를 기반으로 하고 샘플 입력을 q762948 파일로 사용하면 간단한 awk 명령을 사용하여 이 작업을 수행할 수 있습니다.

$ head -2 q762948 >result.txt
# dump the comments as required
$ tail +4 q762948 | awk '{c=(NR-1)%4} c==0{p=$1;print ""} c>0{p=$2}{printf p"  "}'>>result.txt    
$ cat result.txt

# Time-averaged data for fix avetimeall
# TimeStep Number-of-rows

1000  2.09024e-14  4.88628  5.69321  
2000  2.10518e-14  8.33702  8.83162  
3000  1.96656e-14  12.1396  11.5835

관련 정보