가급적이면 (bash에서)를 사용하여 구문 분석하고 다시 포맷해야 하는 큰 파일이 있습니다 sed
. 파일에는 로 PATTERN_START
시작하고 끝나는 반복 시퀀스가 포함되어 있습니다 PATTERN_END
. 이 시퀀스는 내가 변경하지 않고 유지해야 했던 다른 텍스트와 혼합되었습니다. 시퀀스에 여러 레코드가 있습니다(1부터 번호가 매겨짐).N, 어디N1~12까지 가능) 레코드는 다음 형식의 한 줄로 시작하는 줄 집합입니다.Record i
나1과 1 사이의 정수입니다.N, 다른 줄( ) 또는 한 줄로 끝납니다. 레코드 길이는 1줄에서 30줄까지 가능합니다.Record (i+1)
PATTERN_END
다음은 입력 파일의 일반적인 표현입니다.
관련 없는 데이터 (줄이 많을 수도 있음) ⎤ 모드_시작 | 기록 1 ⎤ | 데이터 1개 기록 ⎤ (최대 30줄) | (여러번 반복) ︙ ⎦ | (최대 12개 레코드) | 기록 2 | 2개의 데이터를 기록 ⎦ | 패턴_END ⎦ 관련 없는 데이터 (줄이 많을 수도 있음)
그래서 각 레코드의 모든 데이터 행을 PATTERN_START
과 사이에 있는 레코드 에 대해서만 해당 행으로 집계 하고 싶습니다 .PATTERN_END
Record
누구든지 도와줄 수 있나요?
다음은 구문 분석해야 하는 파일의 예와 원하는 결과 유형입니다.
입력하다
Blabla
Blabla
PATTERN_OTHER
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
PATTERN_END
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Data
PATTERN_END
Blabla
Blabla
Blabla
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
PATTERN_END
Blabla
Blabla
PATTERN_OTHER
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 2 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
PATTERN_END
Blabla
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
PATTERN_END
Blabla
Blabla
산출
Blabla
Blabla
PATTERN_OTHER
Record 1 <- was not between PATTERN_START and PATTERN_END tags => not modified
Data
Data
PATTERN_END
Blabla
PATTERN_START
Record 1 Data Data Data <- record data grouped in one line
Record 2 Data Data <- record data grouped in one line
Record 3 Data Data Data Data <- record data grouped in one line
PATTERN_END
Blabla
Blabla
Blabla
Blabla
PATTERN_START
Record 1 Data Data Data <- record data grouped in one line
PATTERN_END
Blabla
Blabla
PATTERN_OTHER
Record 1 <- was not between PATTERN_START and PATTERN_END tags => not modified
Data
Data
Record 2 <- was not between PATTERN_START and PATTERN_END tags => not modified
Data
PATTERN_END
Blabla
Blabla
PATTERN_START
Record 1 Data <- record data grouped in one line
Record 2 Data Data Data <- record data grouped in one line
PATTERN_END
Blabla
Blabla
답변1
이것이 GNU sed로 원하는 것이라고 생각하십시오
sed -n '/^PATTERN_START/,/^PATTERN_END/{
//!{H;/^Record/!{x;s/\n\([^\n]*\)$/ \1/;x}};
/^PATTERN_START/{h};/^PATTERN_END/{x;p;x;p};d
};p' file
설명하다
sed -n #Non printing
'/^PATTERN_START/,/^PATTERN_END/{
#If the line falls between these two patterns execute the next block
//!{
#If the previous pattern matched from the line above is not on matched(so skip
the start and end lines), then execute next block
H;
#append the line to the hold buffer, so this appends all lines between
#`/^PATTERN_START/` and `/^PATTERN_END/` not including those.
/^Record/!{
#If the line does not begin with record then execute next block
x;s/\n\([^\n]*\)$/ \1/;x
#Swap current line with pattern buffer holding all our other lines
#up to now.Then remove the last newline. As this only executed when
#record is not matched it just removes the newline from the start
#of `data`.
#The line is then put switched back into the hold buffer.
}
#End of not record block
};
#End of previous pattern match block
/^PATTERN_START/{h};
#If line begins with `PATTERN_START` then the hold buffer is overwritten
#with this line removing all the previous matched lines.
/^PATTERN_END/{x;p;x;p}
#If line begins with `PATTERN_END` the swap in our saved lines, print them,
#then swap back in the PATTERN END line and print that as well.
;d
#Delete all the lines within the range, as we print them explicitly in the
#Pattern end block above
};p' file
# Print everything that's not in the range print, and the name of the file
답변2
이것이 내가 생각해 낼 수 있는 최선의 방법입니다:
sed -n '/^PATTERN_START/, /^PATTERN_END/{
/^PATTERN_START/{x;s/^.*$//;x};
/^Record/{x;/^\n/{s/^\n//p;d};s/\n/ /gp};
/^PATTERN_END/{x;/^\n/{s/^\n//p;d};s/\n/ /gp;g;p};
/^Record/!H
};
/^PATTERN_START/, /^PATTERN_END/!p'
설명하다
나는 당신이 의 홀드 공간(Hold Space)과 패턴 공간(Pattern Space)의 개념을 잘 알고 있다고 가정합니다 sed
. 이 솔루션에서는 패턴 공간에서 많은 조작을 수행하게 됩니다. 따라서 첫 번째 요점은 -n
자동 인쇄를 비활성화하고 필요한 경우 인쇄하는 옵션을 사용하는 것입니다.
첫 번째 작업은 선 사이의 모든 선을 연결하는 것입니다 Record
.
다음 파일을 고려하십시오.
a
b
Record 1
c
d
Record 2
e
f
Record 3
선을 연결한 후 우리는 그것을 원합니다
a
b
Record 1 c d
Record 2 e f
Record 3
그래서 계획은 다음과 같습니다.
- 행을 읽고 이를 예약된 공간에 추가합니다.
- 줄이 로 시작하면
Record
이전 레코드가 완료되고 새 레코드가 시작되었음을 의미합니다. 그래서 예약된 공간을 인쇄하고 새로 고치고 1번 지점부터 다시 시작합니다.
/^Record/!H
포인트 1은 코드 (명령의 5행) 로 구현됩니다 . "줄이 로 시작하지 않으면 Record
예약된 공간에 새 줄을 추가하고 예약된 공간에 이 줄을 추가합니다"라는 의미입니다.
포인트 2는 /^Record/{x;s/\n/ /gp;} 코드로 달성할 수 있습니다. 여기서 홀드 x
공간과 패턴 공간이 교체되고, s
명령은 \n
모든 s를 s로 바꾸고 ,
p
플래그는 패턴 공간을 인쇄합니다. 이를 사용하는 x
또 다른 이점 은 이제 예약된 공간에 현재 Record
행이 포함되어 지점 1과 2에서 또 다른 루프를 시작할 수 있다는 것입니다.
그러나 문제가 있습니다. 주어진 예에서 첫 번째 줄 앞에는 두 줄 ab 가 있습니다 Record
. 우리는 이러한 영역을 \n
대체하고 싶지 않습니다 . 로 시작하지 않으므로
Record
포인트 1당 \n
공간을 보존하기 위해 추가한 다음 해당 줄을 추가합니다. 따라서 예약된 공간의 첫 번째 문자가 이면 이전에 해당 문자가 발생하지 않았음을 \n
의미하므로 이를 로 바꾸면 안 됩니다 . 이 작업은 명령을 통해 수행됩니다.Record
\n
/^\n/{s/^\n//p;d}
따라서 전체 명령은 다음과 같습니다.
/^Record/{x;/^\n/{s/^\n//p;d};s/\n/ /gp};
Record
이제 두 번째 문제는 행이 행으로 Record
끝나는 것이 아니라 행으로 끝나는 경우에도 행을 연결하려고 한다는 것입니다 PATTERN_END
. 선이 로 시작하더라도 점 2와 정확히 동일한 작업을 수행하고 싶습니다 PATTERN_END
. 그래서 명령은 다음과 같습니다
/^PATTERN_END/{x;/^\n/?s/^\n//p;d};s/\n/ /gp}
그러나 문제가 있습니다. line 의 경우와 마찬가지로 Record
이제 PATTERN_END
해당 줄은 예약된 공간에서 끝납니다. 하지만 우리는 더 이상 전선 연결이 없을 것이라는 것을 알고 있습니다 PATTERN_END
. 그래서 우리는 그것을 인쇄할 수 있습니다. 그래서 우리는 PATTERN_END
라인을 패턴 공간으로 가져오고 g
를 사용하여 인쇄합니다 p
. 따라서 최종 명령은 다음과 같습니다.
/^PATTERN_END/{x;/^\n/?s/^\n//p;d};s/\n/ /gp;g;p}
다음 문제는 라인입니다 PATTERN_START
. 위의 설명에서는 처음에는 예약된 공간이 비어 있다고 가정합니다. 그런데 잠시 후 PATTERN_END
예약된 공간에 물건이 들어옵니다. (그것은 단지 PATTERN_END
철사입니다). 를 사용하여 새 루프를 시작할 때 PATTERN_START
예약된 공간을 지우고 싶습니다.
따라서 우리가 하는 일은 을 만났을 때 PATTERN_START
홀드 공간과 패턴 공간의 내용을 교환하고 패턴 공간을 비운 다음 다시 교환하는 것입니다. 덕분에 공간을 깨끗하게 유지할 수 있습니다. 이것이 바로 다음 명령이 수행하는 작업입니다.
/^PATTERN_START/{x;s/^.*$//;x}
마지막으로 우리는 PATTERN_START
행과 PATTERN_END
행 사이에서 이 모든 작업을 수행하고 싶습니다. 다른 것들은 그냥 인쇄하면 됩니다. 이 작업은 명령을 통해 수행됩니다.
/^PATTERN_START/, /^PATTERN_END/{
----above commands go here----
};
/^PATTERN_START/, /^PATTERN_END/!p
이 모든 것을 종합하면 최종 명령이 제공됩니다. :)
답변3
다른 방법 sed
:
sed '/PATTERN_START/,/PATTERN_END/{ # in this range
//!{ # if not start or end of range
/^Record/{ # if line matches Record
x # exchange pattern and hold space
/^$/d # if pattern space is empty, delete it
s/\n/ /g # replace newlines with spaces
}
/^Record/!{ # if line doesn't match Record
H # append it to hold space
d # then delete it
}
}
/PATTERN_END/{ # at end of range
x # exchange pattern and hold space
s/\n/ /g # replace newlines with space
G # append hold space to pattern space
x # exchange again
s/.*// # empty pattern space
x # exchange again > empty line in hold space
}
}' infile
또는
sed '/PATTERN_START/,/PATTERN_END/{ # same as above
//!{ # same as above
: again
N # pull the next line into pattern space
/\nRecord/!{ # if pattern space doesn't match this
/\nPATTERN_END/!{ # and doesn't match this either
s/\n/ / # replace newline with space
b again # go to : again
}
}
P # print up to first newline
D # then delete up to first newline
}
}' infile
한 줄:
sed '/PATTERN_START/,/PATTERN_END/{//!{/^Record/{x;/^$/d;s/\n/ /g};/^Record/!{H;d}};/PATTERN_END/{x;s/\n/ /g;G;x;s/.*//;x}}' infile
그리고
sed '/PATTERN_START/,/PATTERN_END/{//!{: again;N;/\nRecord/!{/\nPATTERN_END/!{s/\n/ /;b again}};P;D}}' infile
답변4
세 가지 버전을 만들었습니다.
v1
sed -e'/^PATTERN_START/!b' -e:n -eN \
-e'/\nPATTERN_END$/!bn' -eh\;s/// \
-e'x;s/\n[[:print:]]*$//;x' \
-e's/\(\nRecord [[:print:]]*\)\{0,1\}\n/\1 /g' \
-e'G;P;D' data
저거 출력되네모두편집된 파일만 적용Record
사이에 나타나는 선PATTERN_{START,END}
.
v2
sed -ne'/\n/P;:n' \
-e'/^PATTERN_[OS]/!D' -eN \
-e'/\nPATTERN_END$/!bn' -es/// \
-e'/^PATTERN_S/s/\(\nRecord [[:print:]]*\)\{0,1\}\n/\1 /g' \
-eG\;D ./data ###<gd data> haha
어느 것이 인쇄됩니까?Record
내의 모든 줄PATTERN_{(START|OTHER),END}
하지만 적용됩니다편집하다그 일이 일어난PATTERN_{START,END}
.
v3
sed -ne'/\n/P;:n' \
-e'/^PATTERN_START/!D' -eN \
-e'/\nPATTERN_END$/!bn' -es/// \
-e's/\(\nRecord [[:print:]]*\)\{0,1\}\n/\1 /g' \
-eG\;D ./data
그리고 그거오직편집 및오직인쇄Record
사이에 나타나는 선PATTERN_{START,END}
.
다음은 입력 샘플을 실행한 후 각각에 대한 출력입니다. 출력 샘플은 가장 짧은 것부터 가장 긴 것까지 역순으로 표시됩니다.
v3
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
v2
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 2 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
v1
Blabla
Blabla
PATTERN_OTHER
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
PATTERN_END
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data Data
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Record 3 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Data
Blabla
Blabla
Blabla
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Blabla
Blabla
PATTERN_OTHER
Record 1 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
Data
Record 2 <- record not between PATTERN_START and PATTERN_END tags => do not touch it
Data
PATTERN_END
Blabla
Blabla
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line Data Data Data
PATTERN_START
Record 1 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Record 2 <- record between PATTERN_START and PATTERN_END tags => to put in one line
Data
Data
Data
Blabla
Blabla