블록의 특정 행이 있는 행 블록을 정렬하는 방법은 무엇입니까?

블록의 특정 행이 있는 행 블록을 정렬하는 방법은 무엇입니까?

다음과 같은 데이터가 포함된 파일이 있습니다.

BEGIN
hello2
5
world1
END
BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END

블록 내의 숫자를 기준으로 다음과 같은 방식으로 행을 정렬하고 싶습니다. 이 숫자는 분리되어 있고 고유합니다.

BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END
BEGIN
hello2
5
world1
END

나는 sed와 awk를 사용하여 블록을 인쇄하는 방법을 알고 있습니다. 그게 다야.

    # Prints the blocks including the BEGIN and END tags
    cat file | sed -n '/^BEGIN$/,/^END$/p'

    # Prints the blocks exluding the BEGIN and END tags
    awk '/^BEGIN$/ {show=1;next} /^END$/{show=0}  { print }' file

답변1

GNU awk 사용:

gawk '
    BEGIN { RS="\nEND\n"; ORS = RS; FS = "\n" }
    { record[$3] = $0 }
    END {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        for (val in record) print record[val]
    }
' file

귀하의 데이터에 따르면 BEGIN과 숫자 사이에는 항상 한 줄만 있다고 가정합니다.

PROCINFO줄은 "records" 배열을 반복하는 방법을 정의합니다. 바라보다https://www.gnu.org/software/gawk/manual/html_node/Controlling-Scanning.html

답변2

줄이 나타날 때 마다 BEGIN별도의 핸들을 사용하여 파일의 다음 숫자 줄을 개별적으로 읽습니다 getline. 두 개의 접두사, 즉 이전에 검색된 값을 사용하여 파일의 각 줄을 인쇄합니다.그리고현재 레코드의 파일 레코드 번호입니다(따라서 동일한 블록 내의 모든 행은 BEGIN .. END블록 내에 포함된 번호에 해당하는 접두사 1의 동일한 값을 갖습니다). 접두사를 삭제하여 접두사 기반 순서를 처리하도록 외부 sort및 유틸리티 프로그램 에 이를 제공합니다 .cut

awk '/BEGIN/{"awk \\$0+0==\\$0 "FILENAME | getline x}
{print x"~"FNR"~"$0 | "sort -k1,1n -k2,2n -t~ | cut -f3- -d~"}' file
BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END
BEGIN
hello2
5
world1
END

답변3

첫 번째 줄은 텍스트 블록을 한 줄씩 집계하고 후속 정렬 기준으로 사용할 숫자를 찾으려고 시도합니다. if 절이 if($0+0==$0)숫자를 찾으면 해당 값은 true입니다.

두 번째 블록은 입력에서 "END"를 찾으면 실행되므로 블록을 연관 배열에 저장하고 블록에서 찾은 번호로 인덱싱합니다.

awk '{block=block"\n"$0; if($0+0==$0) num=$0;} 
/^END$/ {blks[num]=block; block=""} 
END {for(key in blks) print blks[key]}' file

마지막 줄은 입력 파일의 끝에 도달하면 배열의 각 항목을 인쇄합니다. 연관 배열은 이미 정렬되어 있으므로(내부적으로 작동하는 방식) 이를 반복하고 각 항목을 인쇄하기만 하면 됩니다.

예를 들어 다음 awk스크립트를 확인하세요.

echo | awk '{a[2]="b"; a[1]="a"; a[3]="c"; for(key in a) print a[key];}'

다음과 같이 출력됩니다.

a
b
c

내 대답에서는 각 블록 앞에 추가 항목을 인쇄하는데 \n그게 문제가 아닌 것 같습니다. 예제의 출력은 다음과 같습니다.

BEGIN
hello4
2
world5
END

BEGIN
hello6
4
END

BEGIN
hello2
5
world1
END

추가 줄이 필요하지 않으면 awk스크립트의 첫 번째 블록을 다음으로 바꿉니다.

{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}}

한 줄 버전은 다음과 같습니다.

awk '{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}} /^END$/ {blks[num]=block; block=""} END {for(key in blks) print blks[key]}' file

관련 정보