sed: 한 줄 입력 시 실패하지 않고 전체 파일을 패턴 공간으로 읽습니다.

sed: 한 줄 입력 시 실패하지 않고 전체 파일을 패턴 공간으로 읽습니다.

전체 파일을 패턴 공간으로 읽는 것은 줄 바꿈 등을 바꾸는 데 유용합니다. 그리고 다음을 제안하는 많은 예가 있습니다.

sed ':a;N;$!ba; [commands...]'

그러나 입력에 한 줄만 포함되어 있으면 실패합니다.

예를 들어 두 줄의 입력이 있는 경우 각 줄은 대체 명령의 영향을 받습니다.

$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt

그러나 단일 라인 입력을 사용하는 경우,아니요교체를 수행합니다.

$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc

sed이 문제 없이 모든 입력을 한 번에 읽는 명령을 어떻게 작성할 수 있습니까 ?

답변1

전체 파일을 패턴 공간으로 읽어들이는 데에는 여러 가지 이유가 있습니다. 마지막 줄을 둘러싼 질문의 논리 문제는 일반적입니다. 이는 행 주기와 관련이 있습니다. sed더 이상 행이 없고 EOF가 발생하면 sed종료되며 처리가 종료됩니다. 따라서 마지막 행에 있고 sed다른 행을 가져오라고 지시하면 거기서 멈추고 더 이상 아무 작업도 수행되지 않습니다.

즉, 전체 파일을 패턴 공간으로 읽어야 한다면 어쨌든 다른 도구를 고려해 볼 가치가 있을 것입니다. 사실은 sed이름이 같은 것입니다개울편집기 - 한 번에 한 줄 또는 하나의 논리적 데이터 블록을 작업하도록 설계되었습니다.

전체 파일 블록을 더 잘 처리하는 유사한 도구가 많이 있습니다. 예를 들어, edand는 대부분의 작업을 수행할 수 있으며 그 외에도 훨씬 더 많은 작업을 수행할 수 있지만 출력으로 변환하는 동안 입력 스트림에서 작업할 뿐만 아니라 파일 시스템에서도 작업을 수행합니다. 임시 백업 파일을 유지합니다. 해당 작업은 필요에 따라 디스크에 버퍼링되며 파일 끝에서 갑자기 종료되지 않습니다.exsedsed(버퍼 압력 하에서는 훨씬 덜 빈번하게 파열됩니다). 또한 sed라인 표시, 실행 취소, 명명된 버퍼, 조인 등과 같이 스트리밍 컨텍스트에서는 이해되지 않는 많은 유용한 기능을 제공합니다.

sed가장 큰 장점은 데이터를 읽은 후 즉시 빠르고 효율적이며 지속적으로 처리할 수 있다는 것입니다. 파일을 삼키면 버립니다.그리고언급한 마지막 줄 문제뿐만 아니라 버퍼 오버플로 및 성능 저하와 같은 극단적인 경우 문제가 발생하는 경향이 있습니다. 구문 분석하는 데이터의 길이가 늘어남에 따라 일치 항목을 열거할 때 정규식 엔진의 처리 시간이 늘어납니다.기하급수적으로.

마지막 요점과 관련하여, 참고: 예시 사례 s/a/A/g는 순진한 예시일 가능성이 높으며 입력에서 수집하려는 실제 스크립트가 아닐 수도 있지만 익숙해지는 데 시간을 투자할 가치가 있다는 것을 알 수 있습니다 y///. g한 문자를 전역적으로 다른 문자로 자주 바꾸는 경우 이는 y매우 유용할 수 있습니다. 이는 대체가 아닌 변환이며 정규식을 암시하지 않기 때문에 훨씬 빠릅니다. 후자는 빈 주소를 유지하고 반복하려고 할 때에도 유용합니다. //왜냐하면 빈 주소에 영향을 주지는 않지만 영향을 받을 수 있기 때문입니다. 어쨌든 y/a/A/이것은 동일한 작업을 수행하는 더 쉬운 방법입니다. 예를 들어 y/aA/Aa/한 줄의 모든 대문자/소문자를 바꾸는 등의 교체도 가능합니다.

또한 귀하가 설명하는 동작은 어떤 방식으로든 발생해야 하는 동작이 아니라는 점에 유의해야 합니다.

info sedGNU에서일반적인 버그 보고서부분:

  • N명령은 마지막 줄에 있습니다

    • sed대부분의 Exit 버전은 명령이 파일의 마지막 줄에서 실행될 때 아무것도 인쇄하지 않습니다. 물론 명령 스위치가 지정되지 않은 한, NGNU는 sed종료하기 전에 모드 공간을 인쇄합니다. -n이 선택은 의도적으로 설계된 것입니다.

    • 예를 들어, 의 동작은 sed N foo barfoo의 행 수가 짝수인지 홀수인지에 따라 달라집니다. 또는 패턴 일치 후 다음 몇 줄을 읽는 스크립트를 작성할 때 전통적인 구현 sed에서는 ./foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }/foo/{ N;N;N;N;N;N;N;N;N; }

    • 어쨌든 가장 쉬운 해결 방법은 $d;N레거시 동작에 의존하는 스크립트에서 이를 사용하거나 POSIXLY_CORRECT변수를 null이 아닌 값으로 설정하는 것입니다.

환경 POSIXLY_CORRECT변수가 언급되는 이유는 POSIX가 sed시도 중 EOF를 만나면 N출력 없이 종료해야 한다고 명시하고 있지만, 이 경우 GNU 버전은 의도적으로 표준을 위반하기 때문입니다. 또한 위의 동작이 합리적이더라도 오류 조건이 스트림 편집 중 하나라고 가정하면 전체 파일이 메모리에 저장되지 않습니다.

이것기준정의된 N동작:

  • N

    • 삽입된 줄눈을 사용하여 추가된 자료를 원래 자료에서 분리하여 다음 입력 줄(종료 \n줄 줄임 제외)을 패턴 공간에 추가합니다. \n현재 줄 번호가 변경됩니다.

    • 다음 입력 줄을 사용할 수 없는 경우 N명령 동사는 스크립트 끝으로 분기하고 새 루프를 시작하거나 패턴 공간을 표준 출력에 복사하지 않고 종료해야 합니다.

:이 시점에서 질문에 시연된 몇 가지 다른 GNU-isms, 특히 태그, b목초지 및 {기능 컨텍스트 괄호 의 사용이 있습니다 }. 경험상 임의의 인수를 허용하는 모든 명령은 스크립트의 다음 줄에서 구분되는 sed것으로 이해됩니다 . \n그래서 주문은...

:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...

sed... 읽는 구현 에 따라 모두 비정상적으로 수행될 가능성이 높습니다 . 이식 가능한 경우 다음과 같이 작성해야 합니다.

...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}

r, w, t, a, i및 에도 동일하게 적용됩니다.c (아마 지금은 잊어버린 내용이 더 있을 것임). 거의 모든 경우에 다음과 같이 작성할 수도 있습니다.

sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
    "//{ do arbitrary list of commands" -e \}

...여기서 새 -e실행 문은 \n줄줄 구분 기호를 나타냅니다. 따라서 GNU info텍스트에서는 다음을 권장합니다.전통적인 sed구현에서는 다음을 수행해야 합니다.:

/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }

...사실 그래야 하는데...

/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}

...물론 이것도 사실이 아니다. 이런 식으로 스크립트를 작성하는 것은 약간 어리석은 일입니다. 다음과 같이 동일한 목적을 달성하는 더 간단한 방법이 있습니다.

printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
         //!g;x;$!d;:nd' -e 'l;$a\' \
     -e 'this is the last line' 

...인쇄:

foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line

... 대부분의 명령과 마찬가지로 test 명령은 sed반환 레지스터를 플러시하는 라인 사이클에 의존하고 라인 사이클이 여기서 대부분의 작업을 수행하도록 허용되기 때문입니다. 이는 파일을 읽을 때 발생하는 또 다른 절충안입니다. 행 주기가 다시 플러시되지 않으므로 많은 테스트가 비정상적으로 작동합니다.

위 명령은 실행 시 읽는 내용을 확인하기 위해 몇 가지 간단한 테스트를 수행하기 때문에 중복 입력의 위험이 없습니다. 이전 의 경우 H모든 행이 예약된 공간에 추가되지만 행이 일치하면 이전 공간을 /foo/덮어씁니다 . h다음으로 버퍼가 변경되고, 버퍼의 내용이 마지막 주소 지정 패턴과 일치하면 x조건부 대체가 시도됩니다. 즉, 예약된 공간의 세 번째 개행 문자를 자신으로 바꾸고 결과를 인쇄해 보세요.s///////s/\n/&/3p만약에현재 경기를 위한 공간을 확보하세요 /foo/. 성공 하면 t스크립트는 ook 작업을 수행 하고 스크립트를 종료하는 notelete 태그로 분기됩니다.dl

그러나 두 /foo/개행 문자와 세 번째 개행 문자가 예약된 공간에서 함께 일치하지 않는 경우 //!g일치하지 않으면 버퍼를 덮어쓰고 /foo/, 일치하는 경우 \n개행 문자가 일치하지 않으면 버퍼를 덮어씁니다.(따라서 /foo/자체적으로 대체됨). 이 미묘한 작은 테스트는 긴 "아니오" 동안 버퍼가 불필요하게 채워지는 것을 방지 /foo/하고 입력이 쌓이지 않기 때문에 프로세스가 빠르게 유지되도록 보장합니다. 아무것도 없거나 /foo/실패 하는 경우 //s/\n/&/3p버퍼가 다시 교체되고 마지막 줄을 제외한 모든 줄이 삭제됩니다.

$!d마지막 줄(마지막 줄)은 여러 상황을 쉽게 처리할 수 있는 하향식 스크립트를 만드는 방법을 간단하게 보여줍니다 . sed일반적인 접근 방식은 가장 일반적인 사례에서 시작하여 가장 구체적인 방향으로 원치 않는 사례를 잘라내는 것인 경우 마지막에 필요한 다른 데이터와 함께 스크립트에 포함될 수 있기 때문에 극단적인 사례를 처리하는 것이 더 쉽습니다. 모든 것이 당신 주위에 둘러싸여 있고, 당신이 원하는 데이터만 남습니다. 그러나 폐쇄 루프에서 이러한 극단적인 경우를 얻는 것은 훨씬 더 어려울 수 있습니다.

따라서 제가 마지막으로 말하고 싶은 것은 다음과 같습니다. 실제로 전체 파일을 추출해야 하는 경우 라인 사이클을 사용하여 작업을 일부 줄일 수 있습니다. 일반적으로 Next 및 next를 사용합니다 .시야- 그들이 앞으로 나아갈 때첫 번째행 기간. 루프에서 닫힌 루프를 중복적으로 구현하는 대신( sed행 루프는 단순한 읽기 루프이므로) 입력을 무분별하게 수집하는 것이 목적이라면 다음과 같이 하는 것이 더 쉬울 수 있습니다.

sed 'H;1h;$!d;x;...'

...이렇게 하면 전체 파일이 수집됩니다. 그렇지 않으면 시도가 실패합니다.


N마지막 줄의 동작에 대한 참고 사항...

테스트할 도구는 없지만 N읽으면서 이것을 고려하십시오.제자리에편집 중인 파일이 다음에 읽을 스크립트 파일인 경우 편집 동작이 달라집니다.

답변2

N명령이 패턴 일치 $!(마지막 줄 아님) 앞에 오고 작업을 수행하기 전에 sed가 종료되기 때문에 실패합니다 .

질소

패턴 공간에 개행 문자를 추가한 후 패턴 공간에 다음 입력 줄을 추가합니다.더 이상 입력이 없으면 sed는 더 이상 명령을 처리하지 않고 종료됩니다..

Nb패턴 뒤에 및 명령을 그룹화하기 만 하면 한 줄 입력을 처리하도록 쉽게 수정할 수 있습니다(어떤 경우에도 실제로는 훨씬 더 명확합니다).

sed ':a;$!{N;ba}; [commands...]'

작동 방식은 다음과 같습니다.

  1. :a'a'라는 라벨을 만듭니다.
  2. $!마지막 줄이 아니라면
  3. N패턴 공간에 다음 라인 추가(다음 라인이 없으면 종료) 및 ba분기(이동) 레이블 "a"

불행하게도 GNU 확장에 의존하기 때문에 이식성이 없지만 @mikeserv가 제안한 다음 대안은 이식성이 있습니다.

sed 'H;1h;$!d;x; [commands...]'

답변3

@mikeserv가 철저하게 설명했듯이 N은 이에 적합하지 않습니다.

이 조각은 전체 파일을 누적하며 이를 스크립트의 나머지 부분에 대한 접두사로 사용할 수 있습니다.

H;$!d;x;s/^\n//

마지막 줄을 읽을 때까지 H를 사용하여 파일을 누적합니다.

*GNU sed로 테스트된 사용 예) 누락된 후행 \n을 참고하세요.

$ printf 'a\nb\nc' | sed -e 'H;$!d;x;s/^\n//' -e 's/^/[/;s/$/]/'
[a
b
c]$ echo $?
0

관련 정보