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


9

전체 파일을 패턴 공간으로 읽는 것은 개행을 대체하는 데 유용합니다. & c. 다음을 조언하는 많은 경우가 있습니다.

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한 번에 모든 입력을 읽는 명령을 작성하면 이 문제가 발생하지 않습니다.


실제 질문이 포함되도록 질문을 편집했습니다. 원하는 경우 다른 답변을 기다릴 수도 있지만 최상의 답변을 수락 된 것으로 표시합니다 (상단 화살표 버튼 바로 아래에있는 답변 왼쪽의 파이프 버튼 참조).
John1024

@ John1024 감사합니다. 예가 있습니다. 이런 종류의 것을 찾는 것은 "모든 것이 잘못되었다"고 생각하는 경향이 있지만 우리 중 일부는 포기하지 않아서 기쁘다. :}
dicktyr

2
세 번째 옵션이 있습니다! GNU의 sed -z옵션을 사용하십시오 . 파일에 null이 없으면 파일 끝까지 읽습니다! 이것에서 발견 : stackoverflow.com/a/30049447/582917
CMCDragonkai

답변:


13

전체 파일을 패턴 공간으로 읽는 것이 잘못 될 수있는 모든 이유가 있습니다. 마지막 줄을 둘러싼 질문의 논리 문제는 일반적인 문제입니다. sed더 이상 라인이없고 sedEOF가 발생 하면 라인 사이클 과 관련 이 있습니다 . 처리가 종료됩니다. 마지막 줄에 있고 sed다른 줄 을 가져 오라고 지시 하면 바로 멈추고 더 이상 할 일이 없습니다.

즉, 전체 파일을 패턴 공간으로 읽어야하는 경우 다른 도구를 고려해 볼 가치가 있습니다. 사실, 스트림 편집기 sed는 시연 적으로 한 번에 라인 또는 논리 데이터 블록을 작동하도록 설계되었습니다.

전체 파일 블록을보다 잘 처리 할 수있는 유사한 도구가 많이 있습니다. ed그리고 ex, 예를 들어, 많은 무엇을 할 수 sed할 수있는 유사한 구문을 - 그리고 많은 다른 외에 -로하지만이 아닌 출력으로 변환하는 동안 입력 스트림에서만 동작하는 sed않는, 그들은 또한 파일 시스템의 임시 백업 파일을 유지 . 그들의 작업은 필요에 따라 디스크에 버퍼링되며 파일 끝에서 갑자기 종료되지 않습니다 (그리고 버퍼 스트레인 하에서 훨씬 덜 자주 영향을 미치는 경향이 있습니다) . 더욱이 이들은 sed스트림 컨텍스트에서 의미가없는 여러 가지 유용한 기능을 제공합니다. 이는 라인 마크, 실행 취소, 명명 된 버퍼, 조인 등과 같은 것입니다.

sed이 회사의 주요 강점은 데이터를 읽는 즉시 빠르고 효율적으로 스트림 처리 할 수 ​​있다는 것입니다. 당신이 파일을 후루룩 소리 내며 먹기 때 당신은 멀리 그것을 던져 그리고 당신은 당신이 언급 마지막 줄 문제 같은 상반된 어려움으로 실행하고 버퍼 오버런 경향이 있고, 한심한 성능 - 길이에 정규 표현식 엔진의 처리 시간 증가는 구문 분석 데이터와 일치하는 항목을 열거 할 때 기하 급수적으로 증가 합니다 .

그 마지막 시점에 관해서는 : 나는 예제 s/a/A/g사례가 매우 순진한 예일 가능성이 높으며 입력을 위해 수집하려는 실제 스크립트가 아닐 수도 있음을 이해하지만 익숙해 지려면 가치가 있음을 알 수 있습니다 y///. g하나의 문자를 다른 문자로 대체하는 경우가 많을 y경우 매우 유용 할 수 있습니다. 대체와 반대로 변환이며 정규 표현식을 암시하지 않기 때문에 훨씬 빠릅니다. 후자의 요점은 빈 //주소는 영향을 미치지 않지만 영향을받을 수 있으므로 빈 주소 를 유지하고 반복하려고 할 때 유용 할 수 있습니다. 어쨌든 y/a/A/동일한 것을 달성하는보다 간단한 방법이며 다음과 같이 스왑이 가능합니다.y/aA/Aa/ 이것은 모든 대문자 / 소문자를 서로 줄로 바꾸는 것입니다.

또한 당신이 묘사하는 행동이 실제로 일어날 일이 아니라는 점에 유의해야합니다.

GNU이다에서 info sed에서 일반적으로보고 된 버그의 섹션 :

  • N 마지막 줄에 명령

    • 명령이 파일의 마지막 행에 발행 sed될 때 아무것도 인쇄하지 않고 대부분의 종료 버전 N. sed물론 -n명령 스위치가 지정되어 있지 않으면 GNU 는 종료하기 전에 패턴 공간을 인쇄 합니다. 이 선택은 의도적으로 설계된 것입니다.

    • 예를 들어, 동작은 sed N foo barfoo에 짝수 줄 또는 홀수 줄이 있는지에 따라 다릅니다. 또는 패턴 일치 다음에 나오는 다음 몇 줄을 읽도록 스크립트를 작성할 때 기존의 구현 에서는 just 대신에 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변수를 비어 있지 않은 값 으로 설정하는 것 입니다.

POSIXLY_CORRECT환경 변수가 언급 POSIX의 경우되도록 지정하기 때문에 sed시도 할 때 만남이를 끝까지 N가 출력없이 종료해야하지만,이 경우에는 표준 GNU 버전 의도적으로 바꿈. 또한 동작이 정당화되었다고 가정하더라도 오류 사례는 전체 파일을 메모리로 슬러 핑하지 않고 스트림 편집 중 하나라는 것입니다.

표준 을 정의 N의 행동을 따라서 :

  • N

    • 추가 된 재료를 원래 재료와 분리하기 위해 \n내장 된 \newline을 사용하여 다음 입력 라인 (종료 ewline을 줄임)을 패턴 공간에 추가합니다. 현재 줄 번호가 변경됩니다.

    • 다음 입력 줄이 없으면 N명령 동사는 스크립트의 끝으로 분기하여 새주기를 시작하거나 패턴 공간을 표준 출력으로 복사하지 않고 종료합니다.

이 노트에서 다른 GNU-isms, 특히 :레이블, b목장 및 {함수 문맥 괄호 사용과 같은 문제가 시연되었습니다 }. 경험상 sed임의의 매개 변수를 허용하는 명령 \n은 스크립트 의 ewline에서 구분하는 것으로 이해됩니다 . 명령은 ...

: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 \}

-execution 문은 \newline 분리 문자 를 나타냅니다 . 따라서 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

... t대부분의 sed명령 과 마찬가지로 est 명령도 리턴 레지스터를 새로 고치는 라인주기에 따라 달라 지기 때문에 여기서 라인주기는 대부분의 작업을 수행 할 수 있습니다. 그것은 파일을 쓸 때 또 다른 절충점입니다. 라인 사이클이 다시 새로 고쳐지지 않으므로 많은 테스트가 비정상적으로 작동합니다.

위의 명령은 읽을 때 읽은 내용을 확인하기 위해 간단한 테스트를 수행하기 때문에 입력에 도달 할 위험이 없습니다. 로 H된 모든 라인은 보류 영역에 추가됩니다하지만 라인이 일치하는 경우 /foo/는 덮어 h오래된 공간. 다음에 버퍼가 x변경 s///되고 버퍼의 내용이 //마지막으로 처리 된 패턴 과 일치하면 조건부 폐기가 시도됩니다 . 다른 단어에서 //s/\n/&/3p시도 자체 보류 공간에서 세 번째로 줄 바꿈을 교체하고 결과를 인쇄하는 경우 보류 공간은 일치합니다 /foo/. 이 t스크립트가 성공하면 스크립트는 not delete 레이블로 분기됩니다.이 레이블은 look을 수행하고 스크립트를 마무리합니다.

두하는 경우 /foo/및 제 3 개행 후, 그래도 유지 공간에서 서로 일치시킬 수없는 //!g경우에는 버퍼를 덮어 /foo/일치하지 않거나,이 일치하는 경우 경우,이 버퍼를 덮어 \newline가 일치하지 하여 대체 ( /foo/하여 자체) . 이 작은 미묘한 테스트는 버퍼를 오랫동안 채우지 않아도 불필요하게 채워 /foo/지지 않도록하고 입력이 쌓이지 않기 때문에 프로세스가 빠르게 유지되도록합니다. no /foo/또는 //s/\n/&/3pfail 경우에 이어 버퍼가 다시 스왑되고 모든 행이 있지만 마지막 행이 삭제됩니다.

마지막 줄 $!d- 마지막 줄 은 sed여러 사례를 쉽게 처리하기 위해 하향식 스크립트 를 만드는 방법을 간단히 보여줍니다 . 가장 일반적인 방법으로 시작하여 가장 구체적인 방법으로 작업하는 원치 않는 사례를 제거하는 일반적인 방법 인 경우, 원하는 다른 데이터를 사용하여 스크립트의 끝까지 넘어갈 수 있기 때문에 엣지 사례를보다 쉽게 ​​처리 할 수 ​​있습니다. 원하는 데이터 만 남게됩니다. 그러나 닫힌 루프에서 이러한 엣지 케이스를 가져와야하는 것은 훨씬 더 어려울 수 있습니다.

그리고 마지막으로 말해야 할 것이 있습니다. 만약 당신이 정말로 전체 파일을 가져와야한다면, 줄주기에 의존하여 당신을 위해 약간의 일을 할 수 있습니다. 일반적으로 Next 및 next는 선순환 보다 앞서 있기 때문에 미리보기에 사용 합니다. 회선주기가 단순한 읽기 루프 이기 때문에 루프 내에서 폐쇄 루프를 중복 구현하는 대신 목적이 입력을 무차별 적으로 수집하는 것이라면 다음과 같이하는 것이 더 쉽습니다.sed

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

... 전체 파일을 수집하거나 파산하려고합니다.


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

테스트 할 도구가 없지만 편집 한 파일이 다음 판독을위한 스크립트 파일 인 N경우 읽고 편집 할 때 내부 편집이 다르게 동작 한다는 점을 고려 하십시오.


1
무조건적인 H우선 순위를 두는 것은 사랑 스럽습니다.
jthill

@mikeserv 입력 해 주셔서 감사합니다. 회선주기를 유지하면 잠재적 인 이점을 볼 수 있지만 어떻게 작동하지 않습니까?
dicktyr

@ dicktyr 잘, 구문은 :a;$!{N;ba}위에서 언급 한 바와 같이 몇 가지 지름길 을 취 합니다. 익숙하지 않은 시스템에서 정규 표현식을 실행할 때 장기적으로 표준 양식을 사용하는 것이 더 쉽습니다. 그러나 이것이 실제로 의미하는 것은 아닙니다. 폐쇄 루프를 구현합니다. 원치 않는 데이터를 가지 치기하고 원치 않는 데이터를 정리하고주기를 수행하여 원하는대로 쉽게 중간에 들어갈 수는 없습니다. 그것은 하향식 일과 같습니다. 모든 일 sed이 방금 한 일의 직접적인 결과입니다. 어쩌면 다르게 볼 수도 있지만 시도하면 스크립트가 더 쉬워 질 수 있습니다.
mikeserv

11

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

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

패턴 다음 에 단순히 Nand b명령을 그룹화하여 단일 행 입력으로 작업하도록 (그리고 경우에 따라 더 명확하게) 쉽게 고칠 수 있습니다 .

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

다음과 같이 작동합니다.

  1. :a 'a'라는 라벨을 만듭니다
  2. $! 마지막 줄이 아니라면
  3. N다음 행을 패턴 공간에 추가하거나 (다음 행이 없으면 종료 ba) 레이블 'a'를 추가하십시오.

불행히도 (GNU 확장에 의존하기 때문에) 이식성이 없지만 다음 대안 (@mikeserv에서 제안)은 이식 가능합니다.

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

나는 다른 곳에서 정보를 찾지 못했기 때문에 여기에 게시했으며 다른 사람들이 광범위하게 문제를 피할 수 있도록 정보를 제공하고 싶었습니다 :a;N;$!ba;.
dicktyr

게시 해 주셔서 감사합니다! 자신의 대답을 받아들이는 것도 좋습니다. 시스템에서 허용하기 전에 잠시 기다려야합니다.
terdon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.