패턴이 존재하지 않는 한 파일에서 패턴까지 줄을 제거합니다.

패턴이 존재하지 않는 한 파일에서 패턴까지 줄을 제거합니다.

sed다음과 같은 것을 사용 하거나 다음을 수행하여 awk필터를 작성 하려고 합니다 .

  • 주어진 패턴이 입력에 없으면 전체 입력을 출력에 복사합니다.
  • 패턴이 입력에 존재하는 경우 첫 번째 발생 이후의 행만 출력에 복사됩니다.

이것은 "git clean" 필터와 함께 작동하지만 아마도 중요하지 않을 것입니다. 중요한 점은 이렇습니다필요stdin에서 입력이 제공되므로 필터로 구현됩니다.

sed예를 들어 특정 패턴의 행을 삭제하는 방법을 알고 있습니다 . 그러나 어디에도 일치하는 항목이 없으면 1,/pattern/d전체 입력이 삭제됩니다./pattern/

grep -q임시 파일을 생성하고 작업을 수행한 다음 입력으로 무엇을 할지 결정하는 완전한 셸 스크립트를 작성하는 것을 상상할 수 있습니다 . 가능하다면 임시 파일을 만들지 않고 이 작업을 수행하는 것을 선호합니다. git이 자주 호출할 수 있으므로 효율적이어야 합니다.

답변1

파일이 너무 커서 메모리에 맞지 않는 경우 Perl을 사용하여 파일을 읽을 수 있습니다.

perl -0777pe 's/.*?PAT[^\n]*\n?//s' file

PAT원하는 모드로 변경하면 됩니다 . 예를 들어 다음 두 개의 입력 파일과 스키마가 있다고 가정해 보겠습니다 5.

$ cat file
1
2
3
4
5
11
12
13
14
15
$ cat file1 
foo
bar
$ perl -0777pe 's/.*?5[^\n]*\n?//s' file
11
12
13
14
15
$ perl -0777pe 's/.*?10[^\n]*\n?//s' file1
foo
bar

설명하다

  • -pe: 입력 파일을 한 줄씩 읽고, 주어진 스크립트를 -e각 줄에 적용하여 인쇄합니다.
  • -0777: 전체 파일을 메모리에 저장합니다.
  • s/.*?PAT[^\n]*\n?//s: 처음 나타날 때까지 PAT와 줄 끝까지 모든 것을 제거합니다.

더 큰 파일의 경우 파일을 두 번 읽는 것을 피할 수 있는 방법이 없습니다. 그것은 다음과 같습니다:

awk -vpat=5 '{
              if(NR==FNR){
                if($0~pat && !a){a++; next} 
                if(a){print}
              }
              else{ 
                if(!a){print}
                else{exit} 
              }
             }' file1 file1

설명하다

  • awk -vpat=5:실행 awk하고 변수를 pat로 설정합니다 5.
  • if(NR==FNR){}: 첫 번째 파일인 경우.
  • if($0~pat && !a){a++; next}: 행이 값과 일치하고 pat정의 a되지 않은 경우 a행을 추가하고 다음 행으로 이동합니다.
  • if(a){print}: a정의된 경우(파일이 패턴과 일치하는 경우) 행을 인쇄합니다.
  • else{ }: 첫 번째 파일이 아닌 경우(따라서 두 번째 패스입니다).
  • if(!a){print}정의되지 않은 경우 a전체 파일이 필요하므로 모든 줄이 인쇄됩니다.
  • else{exit}: a정의된 경우 첫 번째 단계에서 이미 인쇄되었으므로 파일을 다시 처리할 필요가 없습니다.

답변2

암소 비슷한 일종의 영양grep; cat:

{   grep -m1 'pattern' && 
    cat || ! cat ./infile
}   <./infile

POSIXsed; cat:

{ sed -ne'/PATTERN/q;H;1h;$!d;x;p'; cat; } <infile

암소 비슷한 일종의 영양sed; cat:

{ sed -une'/PATTERN/q;H;1h;$!d;x;p'; cat; } <infile

(그냥 추가하세요 -u)


공유하면 좋아요

위의 모든 명령은 read()상위 프로세스(셸의 파일 설명자)의 파일 설명자이기 때문에 작동합니다. 해당 항목 open()과 해당 하위 항목은 해당 설명자를 상속합니다. 여기에서는 모두 표준 입력으로 해결합니다. 거의 모든 다른 유형의 상속 환경과 다른 경향이 있는 파일 설명자에 대한 한 가지 점은 하위 프로세스가할 수 있는상위 환경의 파일 설명자에 영향을 줍니다.

이것들은 정기적인,lseek()- 유능한./infile-sed(GNU 의 nbuffered 모드 제외 -u). 이는 각 프로세스가 여전히 일부 버퍼링을 수행하지만 작업이 완료되면lseek()설명자는 영향을 받은 마지막 지점으로 돌아갑니다. 그렇지 않으면 항목을 올바르게 정렬하기가 어렵습니다.( dd이 효과를 얻기 위해 사용할 수는 있지만).

동일한 설명자가 다음에도 전달되므로다음shell이고 마지막 하위 프로세스가 오프셋을 변경한 경우 마지막 하위 프로세스가 중지된 지점부터 다음 명령이 즉시 입력됩니다. 그래서 우리는 ...

seq 10 >nums
{   grep -m1 5; cat; } <nums

grep5일치하는 항목이 하나만 인쇄된 후 첫 번째 일치 항목의 입력을 종료하고 cat5 이후의 개행 문자 다음에 stdin을 stdout으로 복사하기 시작합니다.

5
6
7
8
9
10

또 다른 점은 grep반환 값이 입력에서 일치하는 항목을 찾았는지 쉽게 알 수 있다는 것입니다.

{   grep -m1 pattern && 
    cat || ! cat ./infile
}   <./infile

... grep일치하는 항목이 있으면 0을 반환하고, && cat그렇지 않으면 전체를 ||복사합니다.cat./infile출력합니다.


몇 가지 grep


seq 100 >nums
only_after()(
    [ -f "$1" ] && {
    >/dev/null \
    grep -m1 "$2" &&
    cat  ||! cat "$1"
} <"$1")
only_after nums '[89]\{2\}'

89
90
91
92
93
94
95
96
97
98
99
100

grep반환을 통해 표준 입력을 모두 소비했는지 여부가 표시됩니다. true를 반환할 확률이 다음과 같은 경우매우 좋은cat아직 해야 할 일이 남아 있어요(이런 일이 발생하지 않는 유일한 경우는 grep입력의 마지막 줄에서 첫 번째 일치 항목이 발견된 경우입니다. 이 경우 규칙에 따라 아무 것도 인쇄해서는 안 됩니다.). 그러나 일치 항목을 찾는 전체 스트림을 소비하고 실패하면 false를 반환하므로 두 번째 스트림은 ||cat전체 파일을 인쇄합니다.

이와 같이:

seq 5 >nums
only_after nums 8; echo return: "$?"

1
2
3
4
5
return: 1

몇 가지 sed

seq 200 >nums
{ sed -une'/190/q;H;1h;$!d;x;p'; cat; } <nums

191
192
193
194
195
196
197
198
199
200

... ~까지의 이전 공간 sed에 각 입력 행을 쌓습니다.HPATTERNsed입력을 찾아 완전히 종료하여 나머지 내용을 남기 cat거나 $마지막 줄을 찾아 sed p이 시점에 저장한 모든 내용을 인쇄합니다. 이와 같이:

seq 10 >nums
{ sed -une'/190/q;H;1h;$!d;x;p'; cat; } <nums

1
2
3
4
5
6
7
8
9
10

sed불행하게도 메모리 가용성 및 구현 에 따라 쉽게 충돌이 발생합니다 . 또한 GNU는 성능에 상당히 해로운 영향을 미칠 수 있는 nbuffered 모드 sed로 전환하지 않는 한 일반적으로 다른 소프트웨어와 잘 작동하지 않습니다 . 반면에 POSIX는 이런 방식으로 잘 작동하도록 지정되었으므로 확실히 시도해 볼 가치가 있는 방법입니다.-used

lseek()- 입력 가능(예: 파이프)다음도 비슷하게 작동할 수 있습니다.

seq 200 | sed -ne'/195/!{H;1h;$!d;x;:p' -ep -e'};n;bp'

196
197
198
199
200

...또는...

seq 3 | sed -ne'/195/!{H;1h;$!d;x;:p' -ep -e'};n;bp'

1
2
3

제자리에서 편집


바꾸고 싶다면./infile- 다시 말해서제자리에서 편집- 그럼 할 수 있어실제로먼저 임시 파일에 버퍼링하여 다음 위치에 씁니다.

{   g=$(grep -m1  pattern) &&
    cut -c2- <<IN >./infile
$(  printf " %s\n" "$g"    &&
    paste -d\  /dev/null -  )
IN
} <./infile

...패턴을 찾지 못하면 아무런 조치도 취하지 않습니다.안 돼요읽다./infile두 번 이상 - 그러나 성공적인 일치를 위해서는 항상 처리된 꼬리를 완전히 버퍼링합니다../infile다시 쓰기 전에 임시 파일로 출력./infile. 더 구체적으로 말하면 여기에 쉘 문서를 작성하는 infile의 유일한 부분은 다음과 같습니다.뒤쪽에 grep일치. grep경기에서 소비되는 모든 입력계속 소비하다따라서 버퍼의 끝만 임시 버퍼에 저장됩니다.

게다가 대부분의 쉘은 여기서 문서를 지원합니다 . 일반적으로 /tmpLinux 시스템의 /tmp경우 와 마찬가지로 tmpfs이는 해당 시스템에 버퍼링된 부분이 디스크에 전혀 나타나지 않음을 의미합니다. 그러나 공평하게 말하면 커널이 파일 캐싱 등을 처리하는 방식으로 인해 캐시할 메모리가 충분하다는 가정하에 tmpfs에 쓰는 것과 다른 곳에 쓰는 것 사이에는 큰 차이가 없을 것입니다. /tmp어쩌면 좀 더 명확하게 말할 수도 있습니다. 껍질도unlink()바이트가 기록되기 전의 버퍼 파일입니다. 따라서 읽기 및/또는 쓰기 설명자가 열려 있는 동안에만 존재합니다. 청소할 것이 없습니다.


다 감싸줘


이를 위해 작은 프로그램을 작성했습니다 ...

allor()(
        set -f; unset o z i m;  OPTIND=1 IFS="
"
        op()    while   getopts :i:o:m: O               &&
                        case    $O$OPTARG               in
                        ([$z]*|m*[!0-9]*|[!imo]*) ! :   ;;
                        (o+)    o= O=;; (o-)     O=     ;;
                        esac||! o=${o+${o:-$i}}  m=${m:-1}
                do      eval "z=$z${O:-o #} $O=\$OPTARG"||exit
                done

        op "$@";[ -f "${i:?No input specified!}" ]      ||i=
        exec < "${i:?Input is not a regular file!}"     &&
        shift   $((OPTIND-(${#O}+1)))                   &&
        z=$( !  { {     grep -m$m "$@" 2>&3 |
                   >&4  sed  -ne'$=;$s/^/ /p'
                } 3>&1| grep . >&2;}   4>&1 )           &&
        set     ${z:?No match found!}   ${o:+'>"$o"'}   &&
        case    $((m==$1))$o    in      (0"$i") ! :     ;;
        (0*)    <"$i"   eval "  cat $3     &&   ! :"    ;;
        (1*)    <<-i    eval "  cut -c2-   $3"
                $(      printf %s\\n $2;paste /dev/null -)
                i
        esac
)

...몇 가지 옵션 구문 분석 등이 추가됩니다. 기본적으로 grep원하는 매개변수를 전달할 수 있습니다 . 모든 매개변수는 그대로 전달됩니다.와는 별개로-i또는 .-o-m

-i해당 매개변수를 사용하여 입력 파일을 지정할 수 있습니다 . -o-표준 출력에 쓰기(어차피 기본 동작임)를 사용하거나 -o+파일을 제자리에서 편집하거나 쓰기 가능한 경로 이름을 편집할 수 있습니다. 일치 횟수를 지정할 수 있습니다. 즉, 이후의 모든 파일을 가져올 수 있습니다.-i-o-m-m일치 항목을 계산합니다. 입력에서 일치 항목을 많이 찾을 수 없는 경우 전체 파일만 검색합니다. 첫 번째 -[iom]항목이 유효한 스위치가 아니거나 두 번째 항목이 -[io]에 직접 전달되는 모든 인수입니다 grep.

요청된 일치가 성공했는지 여부와 쓰기를 시도하기 전에 출력이 배치되어야 하는 위치를 테스트합니다. 예를 들어, 일치가 실패하고 출력이 다시 다음으로 전달되는 경우./infile아무것도 안 하고 떠날 거야./infile홀로. 일치가 성공하고 outfile과 infile이 동일하면 infile이 단축됩니다. 그러나 일치가 실패하고 출력이 다른 곳으로 향하는 경우 에만 cat출력에 입력 됩니다.

작은 데모:

seq 20 >nums
allor -inums -m2 5

15
16
17
18
19
20

...그리고...

seq 10 >nums
allor -inums -m2 5; echo return: "$?"

1
2
3
4
5
6
7
8
9
10
return: 1

...그리고...

seq 20000 >nums
allor -m1999 -inums -o+ 5$; cat nums

19985
19986
19987
19988
19989
19990
19991
19992
19993
19994
19995
19996
19997
19998
19999
20000

답변3

GNU sed를 사용하면 다음을 수행할 수 있습니다.

:x;/PATTERN/{s/.*//;:z;N;bz};N;bx

7예를 들어, 일치시키려는 패턴으로 를 사용 하고 에서 생성된 데이터를 입력 seq하면 숫자 8에서 20(17 포함)이 인쇄됩니다.

seq 20 | sed ':x;/7/{s/.*//;:z;N;bz};N;bx'

그러면 1부터 6까지 인쇄됩니다.

seq 6 | sed ':x;/7/{s/.*//;:z;N;bz};N;bx'

주석에서 지적했듯이 이는 전체 파일을 메모리로 효과적으로 읽어들이는 것입니다. 귀하의 경우에는 이것이 가능한 일입니다.

또한 현재 사례 8~20에서 추가 선행 줄 바꿈이 출력된다는 경고가 있습니다. 이를 강력하게 제거하는 방법을 알아내려고 노력 중입니다. 이것이 귀하의 응용 프로그램에 중요한지 확실하지 않습니다.

답변4

TxR해결 방법은 명령줄에서 다음과 같습니다.

$ txr -c '@ (아마도)
@(뛰어 넘다)
@(트레일러)
@/무늬/
@(끝)
@(반복하다)
@철사
@(do(릴리스 라인))
@(끝)' -

pattern예: 표시되지 않는 행. 코드 약어:

$ txr -c '@ (아마도)
[...]'-
두번째
Ctrl-DEnter
두번째

pattern발생 시 :

$ txr -c '@ (아마도)
[...]
@(끝)' -
두번째
무늬
무늬
엑스
엑스
와이
와이
Ctrl-DEnter

보시다시피, pattern그런 일이 발생하면 선이 에코되기 시작합니다.

논리는 매우 간단합니다. 포함된 재료는 @(maybe)...@(end)선택적으로 일치합니다. @(skip)여러 줄을 건너뛰는 줄이 있고 그 뒤에 @(trailer)"다음을 후행 컨텍스트로 일치시킵니다(사용하지 않고)"를 의미하는 줄이 있습니다 . (이 기능과 해당 이름은 Lex의 후행 슬래시 컨텍스트에서 영감을 받았습니다.) 제거하면 @(trailer)패턴과 일치하는 줄이 출력에서 ​​제외됩니다.

물론 @/pattern/정규 표현식입니다 . 암시적으로 고정되어 있으므로 전체 줄과 일치해야 합니다. 따라서 다음을 포함하는 행을 일치시키려면 또는 를 abc사용합니다 .@/.*abc.*/@(skip)abc@(skip)

이 패턴이 발생하지 않는 경우 전체 입력이 skip내부적으로 스캔되어 maybe결국 실패합니다. maybe이 실패를 포착하고 숨겨 성공을 달성하세요. 그런 다음 후속 자료는 maybe내부적으로 실패한 원래 입력(즉, 스트림 시작)과 maybe일치됩니다 .

@(repeat)마지막으로 출력 부작용을 포함하는 반복 일치 구조가 있습니다 .

@(data ...)명령을 사용하여 변수에서 현재 데이터 커서(지연 목록 포인터)를 캡처한 start다음 EOF에서 다시 캡처하고 고대 Lisp 함수를 사용하여 ldiff출력을 계산하는 다른 TXR 솔루션 :

$ txr -c '@(maybe)
@(skip)
@/pattern/
@(end)
@(data start)
@(skip)
@(eof)
@(data end)
@(do (tprint (ldiff start end)))' -

선택적으로 일부 줄과 패턴을 일치시킨 후 캡처가 시작됩니다. 패턴이 나타나지 않으면 @(maybe)...@(end)블록이 존재하지 않는 것처럼 데이터의 시작 부분이 캡처됩니다.

즉, "패턴으로 끝나는 일부 줄을 건너뛸 수도 있습니다. 두 경우 모두 위치를 시작으로 표시한 다음 EOF로 점프하고 위치를 끝으로 표시합니다. 시작과 끝 사이의 모든 내용을 인쇄합니다."

관련 정보