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
.