sed 범위가 항상 한 줄만 일치할 수 있는 것은 아닙니다.

sed 범위가 항상 한 줄만 일치할 수 있는 것은 아닙니다.

다음 범위를 고려하십시오. 1,/pattern/첫 번째 줄에서 패턴이 일치하면 범위는 전체 파일과 일치합니다.

$ cat 1.sh
#!/usr/bin/env bash
set -eu
seq 1 4 | sed -rn '1,/'"$1"'/p'
$ ./1.sh 1
1
2
3
4
$ ./1.sh 2
1
2

당신은 무엇을 하시겠습니까?

UPD제가 한 일은 다음과 같습니다(만약에 대비하여):

re='/1/'
seq 1 4 | sed -rn "1{$re{p;q}}; 1,${re}p"

아니면 이거:

seq 1 4 | sed -rn "1{/1/{p;q}}; 1,//p"

답변1

응, 짜증나는 일이야 sed(봐봐sed자주하는 질문대략 이 점). GNU sed( GNU 특정) 를 사용하고 있으므로 -r다음을 수행할 수 있습니다.

 sed -En "0,/$1/p"

(저는 FreeBSD와 같은 다른 도구에서도 지원되고 일관성이 있기 때문에 -E선호합니다 .-rsedgrepPOSIX/Single UNIX 사양 표준의 다음 호)).

더 나은 대안(및 이식 가능)은 다음과 같습니다.

sed "/$1/q"

sed첫 번째 게임이 끝나면 종료(그리고 읽기 중지)하라고 지시합니다.

문제가 없으므로 awk다음과 같이 작성할 수 있습니다.

PATTERN=$1 awk 'NR==1, $0 ~ ENVIRON["PATTERN"]'

( for 와 비슷하지만 sed작성하는 것이 좋습니다):

PATTERN=$1 awk '1; $0 ~ ENVIRON["PATTERN"] {exit}'

답변2

이는 정상적인 동작입니다 sed. POSIX에서sed문서:

sed의 주소

주소는 파일의 입력 줄을 누적 계산하는 10진수, 입력의 마지막 줄 주소를 지정하는 "$" 문자 또는 컨텍스트 주소(sed의 정규 표현식에 설명된 대로 BRE로 구성됨)일 수 있습니다. 그 뒤에 구분 기호(보통 슬래시)가 옵니다.

주소가 없는 편집 명령은 각 패턴 공간을 선택해야 합니다.

주소가 있는 편집 명령은 해당 주소와 일치하는 모든 패턴 공간을 선택해야 합니다.

두 개의 주소가 있는 편집 명령은 첫 번째 주소와 일치하는 첫 번째 패턴 공간부터 두 번째 주소와 일치하는 다음 패턴 공간까지 포함 범위를 선택해야 합니다.. (두 번째 주소의 번호가 첫 번째 선택한 행 번호보다 작거나 같으면 한 행만 선택할 수 있습니다.) 선택한 범위 다음의 첫 번째 행부터 시작하여 sed는 첫 번째 주소를 다시 찾습니다. 그런 다음 이 과정을 반복해야 합니다. 다음 형식의 주소 부분 중 하나 또는 둘 다를 생략하면 정의되지 않은 결과가 생성됩니다.

[주소[,주소]]

sed첫 번째 주소부터 다음 일치하는 주소까지 포함 범위가 인쇄되는 것을 볼 수 있습니다 .

귀하의 경우에는 1,/1/p주소 sed와 일치하므로 첫 번째 줄이 인쇄됩니다 1. 그런 다음 두 번째 줄부터 시작하여 sed는 패턴과 일치하는 두 번째 주소를 검색합니다 /1/. 발견되면 인쇄를 중지하십시오. 두 번째 줄부터 시작하면 일치하는 패턴이 없으므로 나머지를 인쇄하세요 /1/.sed

위와 같이 sed를 사용하는 경우 1./2/p첫 번째 줄을 인쇄하고 두 번째 줄은 패턴과 일치하여 인쇄 /2/하고 sed나머지를 반복합니다. 하지만 1나머지 주소는 일치할 수 없으므로 sed아무것도 인쇄되지 않습니다.

한 가지 예:

$ echo 1 2 3 1 4 1 | tr ' ' $'\n' | sed -rn '1,/1/p'
1
2
3
1

을 사용하므로 GNU sed다음 양식을 사용할 수 있습니다 0,addr2.

0,addr2
              Start  out  in  "matched  first  address"  state, until addr2 is
              found.  This is similar to 1,addr2, except that if addr2 matches
              the very first line of input the 0,addr2 form will be at the end
              of its range, whereas the 1,addr2 form  will  still  be  at  the
              beginning of its range.  This works only when addr2 is a regular
              expression.

따라서 귀하의 명령은 다음과 같습니다.

seq 1 4 | tr ' ' $'\n' | sed -rn '0,/'"$1"'/p'

그 다음에:

$ ./1.sh 1
1

답변3

당신이 할 수 있는 일이 몇 가지 있습니다. 예를 들어 귀하의 의견은 귀하가 다음을 의미한다는 것을 나타냅니다.

...파일의 시작 부분부터 특정 줄(첫 번째 줄)까지 모든 것을 삭제합니다.

다음을 수행할 수 있습니다.

sed -n "/$1"'/,$p'

형태를 반전시키면 됩니다. 위 명령은 특정 줄부터 파일 끝까지만 인쇄합니다.

특정 줄을 인쇄하고 싶지 않다면...

sed -n "/$1"'/,$p' | sed 1d

... 트릭을 수행해야합니다 ...

그렇지 않으면 문제를 직접 해결하고 주기를 직접 처리할 수 있습니다.

seq 20 | sed -ne"/$1"'/!d;:B' -e'n;p;bB'
seq 20 | sed -n "/$1"'/!d;h;n;G;P;D'

두 명령 모두 패턴이 나타날 d때까지 들어오는 각 줄을 삭제합니다 .$1

그런 다음 첫 번째 명령은 n패턴 공간을 외부 입력 라인으로 덮어쓰고 :b레이블을 설정합니다. 그런 다음 p라인을 인쇄하고 라벨 로 돌아가기 n전에 패턴 공간을 외부 라인으로 다시 덮어씁니다 . 파일 끝까지 이런 식으로 반복됩니다. 이 명령은 아마도 두 번째 명령보다 빠르며 더 적은 작업을 수행합니다.b:b

두 번째는 h일치하는 항목으로 이전 공간을 덮어씁니다 $1. 그런 다음 입력의 추가 행으로 패턴 공간을 덮어씁니다 n. 다음으로, G공간을 확보하고 방금 가져온 입력 줄에 추가합니다. 이렇게 하면 두 줄의 순서가 바뀌고 개행 문자로 구분됩니다. 그것은 다음과 같습니다:

  • 1호선 > 공간 확보

  • 2호선 > 1호선

  • 예약공간 >> 2호선

  • = 라인 2\n 라인 1

이 시점에서 패턴 공간에 나타나는 첫 번째 ewline 문자 sed P만 인쇄되고 나머지로 루프를 다시 시작하기 전에 동일한 요소 - end\nD언제나$1첫 번째 일치 라인입니다각 라인. 따라서 일치하는 첫 번째 줄 $1은 다음과 같습니다.언제나패턴 공간에서는안 돼요인쇄.

따라서 다음을 $1인쇄 하면:5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

답변4

quit 명령을 포함하는 범위 다음에 함수 목록을 사용할 때 sed범위를 유지하고 또는 -E옵션을 사용하지 않을 수 있습니다(FreeBSD 및 GNU로 테스트됨).-rsedqsed

printf '%s\n' {1..10} | sed -n '1,/1/{p;q;}'

# your solution adapted to work with FreeBSD sed as well
re='/1/'
printf '%s\n' {1..4} | sed -En "1{$re{p;q;};}; 1,${re}p"
printf '%s\n' {1..4} | sed -En "1{/1/{p;q;};}; 1,//p"

관련 정보