두 패턴 사이의 시작과 끝 인쇄(범위 끝 제외)

두 패턴 사이의 시작과 끝 인쇄(범위 끝 제외)

sed -n "/START PATTERN/,/END PATTERN/p" file.txt패턴을 사용하여 파일을 검색 하고 싶습니다 .

file.txt내용은

~keyword~, ~output~.
~1.~ ~output~.
~2.~ ~output~.
~keyword~, ~output~.
~1.~ ~output~.
~2.~ ~output~.
~3.~ ~output~.
~keyword blablabla~, ~not the output~.
~1.~ ~not the output~.
~2.~ ~not the output~.
~keyword blablabla2~, ~not the output~.
~1.~ ~not the output~.
~2.~ ~not the output~.
~3.~ ~not the output~.
~4.~ ~not the output~.
~blablabla~, ~not the output~.
~1.~ ~not the output~.
~2.~ ~not the output~.
~3.~ ~not the output~.
~4.~ ~not the output~.

내 예상 결과는

~keyword~, ~output~.
~1.~ ~output~.
~2.~ ~output~.
~keyword~, ~output~.
~1.~ ~output~.
~2.~ ~output~.
~3.~ ~output~.

따라서 시작 패턴은 keyword중간에 있고 그 ~뒤에 문자가 .옵니다./~keyword~./

종료 패턴 ~뒤에는 알파벳 문자가 오고 그 다음에는 char 가 옵니다 ..

내가 실행하면 sed -n "/~keyword~./,/[~][[:alpha:]]./p" file.txt출력은 다음과 같습니다.

~keyword~, ~output~.
~1.~ ~output~.
~keyword~, ~output~.
~1.~ ~output~.

두 번째와 세 번째 줄은 출력에 인쇄되지 않으므로 내 질문은 내 접근 방식에 어떤 문제가 있습니까? 제공된 솔루션을 사용하여 영감을 얻었습니다.여기

나는 또한 sed "/~keyword~./,/[~][[:alpha:]]./!d;//d" file.txt빈 출력을 얻으려고 노력했습니다(이 질문에서 영감을 받아)

이 질문은 정규식에 sed를 사용하는 것에 대해 구체적으로 질문했기 때문에 중복으로 표시된 질문과 다릅니다. 이를 염두에 두고 중복이라고 생각되면 중복으로 표시해 주세요.

답변1

sed이 도구가 작업에 적합한지 살펴보겠습니다 .

sed '/^~[[:alpha:]].*/!{               # if line doesn't match this pattern
H                                      # append it to hold space
$!d                                    # and delete it if it's not the last line
b end                                  # else branch to label end
}
//b end                                # if line matches, branch to label end
: end                                  # label end
x                                      # exchange pattern space w. hold space
/^~keyword~.*/p                        # if pattern space matches, print it
d' infile                              # delete pattern space

gnu sed한 줄로 작성할 수 있습니다 .

sed '/^~[[:alpha:]].*/!{H;$!d;b end};//b end;: end;x;/^~keyword~.*/p;d' infile

답변2

사용 중인 것과 같은 패턴으로 구분된 범위는 /P1/,/P2/일치하는 줄에서 시작하고 포함하며 /P1/일치하는 줄에서 끝나며 포함합니다 /P2/.

패턴이 줄의 시작 부분에 고정되어 있지 않으므로( ^정규식에서 선행을 사용할 수 있음) 일치할 수 있습니다.어딘가에줄을 서서.
"종료" 패턴은 /[~][[:alpha:]]./유지하려는 데이터 행과 일치합니다(특히 "~유럽tput" 부분), 따라서 범위는 정확히 첫 번째 데이터 행에서 끝납니다.

나는 범위를 첫 번째 줄에서 끝내도록 제안하려고 했습니다.아니요데이터 패턴과 일치하지만 sed범위가 겹치는 것은 지원되지 않으므로 연속적인 "청크"(예: 예의 청크 1 및 청크 2)를 인쇄할 수 없게 됩니다. (첫 번째 블록에는 두 번째 블록의 첫 번째 줄이 포함됩니다.)

우리 주님이시며 구원자이신 것에 대해 관심을 가져도 될까요 awk? ;)

awk '
    BEGIN {
        inrange = 0
    }
    /^~[[:alpha:]]/ {
        inrange = 0
    }
    /^~keyword~/ {
        inrange = 1
    }
    {
        if (inrange) {
            print
        }
    }'

한 가지 설명은 다음과 같습니다.

  • 위 스크립트는 sed와 마찬가지로 awk입력(file 또는 stdin)을 한 줄씩 구문 분석합니다.
  • 처음에(= 첫 번째 줄을 처리하기 전) "현재 줄을 인쇄해서는 안 됩니다"라는 플래그를 설정합니다.
  • 또한 현재 줄이 "블록 뒤 첫 번째 줄"에 지정한 패턴과 일치하면 플래그를 "인쇄 안 함"으로 설정합니다.
  • 현재 줄이 "블록의 첫 번째 줄"에 대해 지정한 패턴과 일치하면 플래그가 "인쇄 수행"으로 설정됩니다.
  • 플래그에 따라 현재 행을 인쇄하거나 인쇄하지 않습니다.

검사 순서를 다시 정렬하여 "블록 시작" 줄을 제외할 수도 있습니다(예: 먼저 인쇄/인쇄하지 않은 다음 현재 줄이 블록 시작인지 확인).

스크립트의 줄 바꿈 awk도 선택 사항이지만 가독성이 크게 향상됩니다.

답변3

sed이 작업에 적합한 도구가 아닙니다.

...하지만 그렇다고 해서 명령을 실행하는 데 이를 남용할 수 없다는 의미는 아닙니다.

sed -n -e "2,$ p" -e "/^~keyword~./ {s/$/§/;p}" in.txt | sed -n '/^~keyword~./,/^~[[:alpha:]]./ {/^~keyword~..*§$/ {s/§$//; b print}; /^~[[:alpha:]]./b; :print p}'

따라서 한동안 어두운 방에 누워서 그 혐오스러운 것에서 회복한 후에는 다음과 같은 일을 합니다.

우리는 무엇을 달성하고 싶은가?
파일에서 "청크"를 추출합니다. 여기서 각 "청크"는 정규식 R1과 일치하는 줄("시작 줄")로 시작하고 다음 정규식 R2가 발생하기 전의 줄("중지 줄")로 끝납니다. .

그렇다면 sed패턴 범위를 사용하는 것만으로도 문제가 무엇입니까?
R2는 R1의 하위 집합이므로 "끝 라인"이 새 블록의 시작일 수 있습니다. sed겹치는 블록은 지원되지 않습니다.

따라서 R2와 일치하지만 R1과는 일치하지 않는 정규식을 작성하십시오.
길이가 0인 어설션이 필요하지만 sed그런 어설션은 없습니다. (내가 sed이것이 올바른 도구가 아니라고 말한 것을 기억하십니까 ?)

해결 방법: "끝 행"을 찾는 것이 "시작 행"을 뒤덮는 경우 "시작 행"을 복사하면 됩니다.
이것은 작동하지만 첫 번째 "시작 행"을 반복할 수 없습니다. 그렇지 않으면 반복되는 각 쌍을 블록으로 처리합니다. 1

sed -n -e "2,$ p" -e "/^~keyword~./ {s/$/§/;p}" in.txt

= 라인 2에서 시작하는 모든 라인(즉, 라인 1을 제외한 모든 라인)을 인쇄합니다. 라인도 인쇄두 번째R1과 일치하면. s/$/§/그것에 대해서는 나중에 설명하겠습니다 .

이제 명확하게 구분된 블록이 있으므로 패턴 범위를 사용하여 블록 시작과 종결자 사이에 포함된 모든 줄을 인쇄합니다.sed -n '/^~keyword~./,/^~[[:alpha:]]./p'

아 잠깐만요, 여기에는 터미네이터 라인도 포함됩니다.스택 오버플로가 구출됩니다..
하지만 R2와 일치하는 모든 줄을 건너뛸 수는 없습니다. R1 ⊂ R2를 기억하세요. 따라서 종결자 줄을 삭제하면 시작 줄도 삭제됩니다.

"럭키", sed가지가 있습니다. R1과 일치하는 모든 항목을 인쇄하고 R2에 대한 일치 항목을 삭제하는 것은 어떻습니까?그 다음에?

sed -n '/^~keyword~./,/^~[[:alpha:]]./ {/^~keyword~./b print; /^~[[:alpha:]]./b; :print p}'

좋습니다. 이제 종료 라인이 될 때 중복 출발 라인을 인쇄하고 있습니다... 원래 출발 라인과 중복 출발 라인을 구별할 수 있는 방법만 있었다면...

이것이 우리가 이렇게 하는 이유입니다: 반복되는 각 시작 줄의 끝에 하나를 추가합니다 s/$/§/(§'으로 된 반복되는 시작 줄은 결국 블록의 시작 줄이 되고, §'으로 된 줄이 아닌 시작 줄은 결국 블록의 시작 줄이 됩니다). 블록의 시작 라인이 됩니다) §바로 뒤에 다른 블록의 종료 블록이 옵니다.

이제 우리는 더욱 세밀한 검사와 분기를 수행하는 데 필요한 모든 정보를 얻었습니다.

블록 범위 내의 모든 행에 대해...

  • 행이 R1과 일치하고 끝에 §이 있는지 확인하십시오.
    그렇다면 §를 제거하고 점프하여 해당 줄을 인쇄하세요.
  • 그렇지 않은 경우(즉, 점프하지 않는 경우) 모든 추가 명령(인쇄 포함)을 건너뛰어 R2와 일치하는 모든 줄을 삭제합니다.
  • 마지막으로 현재 줄을 인쇄합니다.
{/^~keyword~..*§$/ {s/§$//; b print}; /^~[[:alpha:]]./b; :print p}

최종 결과:

sed -n -e "2,$ p" -e "/^~keyword~./ {s/$/§/;p}" in.txt | sed -n '/^~keyword~./,/^~[[:alpha:]]./ {/^~keyword~..*§$/ {s/§$//; b print}; /^~[[:alpha:]]./b; :print p}'

그러나 파일의 첫 번째 시작 라인(R1과 일치)이 라인 1에 있다고 가정합니다(이 라인은 시작 라인을 복사할 때 제외되는 유일한 라인임을 기억하십시오). 그렇지 않은 경우 깔끔한 쌍을 얻을 수 있지만 데이터는 없습니다.

~keyword~, ~output~.
~keyword~, ~output~.

이 문제를 해결하기 위해 더 많은 일치 항목과 분기를 추가할 수 있지만 실제로는...

그냥 사용하세요 awk.

관련 정보