패턴의 첫 번째 발생과 마지막 발생 사이의 모든 줄을 어떻게 얻을 수 있습니까?

패턴의 첫 번째 발생과 마지막 발생 사이의 모든 줄을 어떻게 얻을 수 있습니까?

foo패턴의 첫 번째 발생부터 패턴의 마지막 발생까지 의 줄만 가져오도록 파일(입력 스트림)을 어떻게 자르나요 bar?

예를 들어 다음 입력을 고려해보세요.

A line
like
foo
this 
foo
bar
something
something else
foo
bar
and
the
rest

나는 다음과 같은 결과를 기대합니다.

foo
this 
foo
bar
something
something else
foo
bar

답변1

sed -n '/foo/{:a;N;/^\n/s/^\n//;/bar/{p;s/.*//;};ba};'

sed 패턴 일치는 /first/,/second/한 줄씩 읽습니다. 일부 라인이 일치하면 /first/이를 기억하고 /second/해당 패턴과 첫 번째로 일치하는 항목을 찾습니다. 동시에 이 모드에 지정된 모든 활동이 적용됩니다. 그 후 파일이 끝날 때까지 프로세스가 계속해서 시작됩니다.

그것은 우리에게 필요한 것이 아닙니다. 마지막으로 일치하는 패턴을 찾아야 합니다 /second/. 그래서 우리는 첫 번째 입구만 보이는 건물을 짓습니다 /foo/. 발견되면 루프가 a시작됩니다. 일치 버퍼에 새 줄을 추가하고 N패턴과 일치하는지 확인합니다 /bar/. 만약 그렇다면, 우리는 그것을 인쇄하고 일치 버퍼를 지운 다음 janyway를 사용하여 루프의 시작 부분으로 점프합니다 ba.

또한 clean buffer를 사용한 후에는 줄바꿈을 제거해야 합니다 /^\n/s/^\n//. 더 나은 해결책이 있을 것이라고 확신합니다. 불행히도 저는 그것을 생각하지 못했습니다.

모든 것이 명확해지기를 바랍니다.

답변2

저는 약간의 Perl을 사용해서 해보겠습니다.

cat <<EOF | perl -ne 'BEGIN { $/ = undef; } print $1 if(/(foo.*bar)/s)'
A line
like
foo
this 
foo
bar
something
something else
foo
bar
and
the
rest
EOF

생산하다

foo
this 
foo
bar
something
something else
foo
bar

답변3

다음은 많은 메모리가 필요하지 않은 2단계 GNU sed 솔루션입니다.

< infile                                     \
| sed -n '/foo/ { =; :a; z; N; /bar/=; ba }' \
| sed -n '1p; $p'                            \
| tr '\n' ' '                                \
| sed 's/ /,/; s/ /p/'                       \
| sed -n -f - infile

설명하다

  • 첫 번째 호출은 infile을 전달하고 첫 번째 발생 과 모든 후속 발생을 sed찾습니다 .foobar
  • sedsed그런 다음 이러한 주소는 두 번의 호출과 한 번의 호출로 구성된 새로운 스크립트로 구성 됩니다 tr. 세 번째 출력은 괄호 없이 sed입니다 [start_address],[end_address]p.
  • 마지막으로 호출이 다시 sed전달되어 infile찾은 주소와 그 사이의 모든 내용이 인쇄됩니다.

답변4

또 다른 접근 방식은 다음과 같습니다 sed.

sed '/foo/,$!d;H;/bar/!d;s/.*//;x;s/\n//' infile

/foo/,$범위의 모든 행( !이 범위에 없는 행은 삭제됨 d) 을 H이전 공간 에 추가 합니다 . bar그런 다음 일치하지 않는 행을 삭제합니다. 일치하는 라인에서 패턴 공간은 비워지고 e는 x예약된 공간으로 변경되며 패턴 공간의 선행 빈 라인은 제거됩니다.

입력량이 많고 bar이런 일이 거의 발생하지 않으면 각 라인을 패턴 공간으로 끌어온 다음 매번 패턴 공간을 확인하는 것보다 훨씬 빠릅니다 bar.
설명하다:

sed '/foo/,$!d                     # delete line if not in this range
H                                  # append to hold space
/bar/!d                            # if it doesn't match bar, delete 
s/.*//                             # otherwise empty pattern space and
x                                  # exchange hold buffer w. pattern space then
s/\n//                             # remove the leading newline
' infile

물론 이것이 파일이고 메모리에 맞는 경우 다음을 간단히 실행할 수 있습니다.

 ed -s infile<<'IN'
.t.
/foo/,?bar?p
q
IN

왜냐하면ed 할 수 있는앞으로 검색그리고뒤에.
쉘이 프로세스 대체를 지원하는 경우 명령 출력을 텍스트 버퍼로 읽을 수도 있습니다.

printf '%s\n' .t. /foo/,?bar?p q | ed -s <(your command)

또는 그렇지 않은 경우 다음을 수행하십시오 gnu ed.

printf '%s\n' .t. /foo/,?bar?p q | ed -s '!your command'

관련 정보