정규식과 일치하는 첫 번째 줄 다음에 파일의 일부를 얻는 방법은 무엇입니까?


169

약 1000 줄의 파일이 있습니다. grep 문과 일치하는 줄 다음에 내 파일 부분을 원합니다.

그건:

$ cat file | grep 'TERMINATE'     # It is found on line 534

따라서 추가 처리를 위해 535 행에서 1000 행으로 파일을 원합니다.

어떻게해야합니까?


34
UUOC (고용이없는 고양이 사용) :grep 'TERMINATE' file
— Jacob

30
나는 그것을 그렇게 사용하는 것처럼 알고 있습니다. 다시 질문으로 돌아 갑시다.
— Yugal Jindle

3
이것은 완벽하게 훌륭한 프로그래밍 문제이며 스택 오버 플로우에 적합합니다.
— aioobe

13
@Jacob 고양이를 전혀 쓸모없는 것은 아닙니다. 그것의 사용은 우리가 사용할 수 있다는 것을 의미 표준 출력에 파일을 인쇄하는 grep대신에 적용 할 전환 무엇을 배울 필요없이 데이터를 읽을의 표준 입력 인터페이스를 grep, 그리고 sed, 그리고 awk, 그리고 pandoc, 그리고 ffmpeg우리가 읽고 싶은 경우 등 파일에서. 파일을 읽는 것과 같은 일을 할 때마다 새로운 스위치를 배울 필요가 없기 때문에 시간이 절약됩니다.
— runeks

@runeks 나는 당신의 감정에 동의합니다. 그러나 당신은 고양이없이 그것을 달성 할 수 있습니다 : grep 'TERMINATE' < file. 어쩌면 좀 더 어려워 질 수도 있습니다-그러나 이것은 쉘 스크립팅이므로 항상 문제가 될 것입니다 :)
— LOAS

답변:


307

다음은 TERMINATE파일 끝까지 일치하는 줄을 인쇄 합니다.

sed -n -e '/TERMINATE/,$p'

설명 : -n 의 비활성화 기본 동작 sed그것에 그 스크립트를 실행 한 후, 각 라인을 인쇄, -e스크립트를 나타내는 sed, /TERMINATE/,$일치하는 첫 번째 행을 의미하는 어드레스 (행)의 범위의 선택이다 TERMINATE(파일의 끝 (그렙 등) 정규식 $) 이며 p현재 행을 인쇄하는 인쇄 명령입니다.

이것은 TERMINATE파일 끝까지 줄 일치를 따르는 줄에서 인쇄 됩니다.
(일치하는 줄을 제외하고 일치하는 줄부터 EOF로)

sed -e '1,/TERMINATE/d'

Explained : 1,/TERMINATE/ 는 TERMINATE정규식과 일치하는 첫 번째 행에 대한 입력의 첫 번째 행을 의미하는 주소 (행) 범위 선택 이며 d현재 행을 삭제하고 다음 행으로 건너 뛰는 delete 명령입니다. 으로 sed기본 동작이 라인을 인쇄하는 것입니다, 그것은 이후의 라인을 인쇄 할 수 TERMINATE 입력의 끝.

편집하다:

전에 줄을 원한다면 TERMINATE:

sed -e '/TERMINATE/,$d'

그리고 TERMINATE단일 패스에서 두 개의 다른 파일에서 전후 에 두 줄을 모두 원한다면 :

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

이전 및 이후 파일에는 종료 행이 포함되므로 다음을 사용해야합니다.

head -n -1 before
tail -n +2 after

편집 2 :

sed 스크립트에서 파일 이름을 하드 코딩하지 않으려면 다음을 수행하십시오.

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

그러나 $마지막 줄 의 의미 를 피해야 쉘이 $w변수 를 확장하려고 시도하지 않습니다 (이제 스크립트 주위에 작은 따옴표 대신 큰 따옴표를 사용합니다).

sed가 파일 이름이 끝나는 것을 알 수 있도록 스크립트에서 파일 이름 뒤에 새 줄이 중요하다는 것을 잊어 버렸습니다.


편집 : 2016-0530

세바스찬 클레멘트 (Sébastien Clément)는 "하드 코딩 TERMINATE된 변수를 어떻게 바꾸 겠습니까?"

일치하는 텍스트에 변수를 만든 다음 이전 예제와 같은 방식으로 수행합니다.

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

이전 예제와 일치하는 텍스트에 변수를 사용하려면

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the 
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

이 경우 텍스트를 변수로 바꾸는 데있어 중요한 점은 다음과 같습니다.

  1. [ ]로 $variablename묶인 변수 ( ) 는 "확장"되지 않지만 [ ] 내부의 변수 는됩니다. 그래서, 당신은 변경해야 할 모든 에 그들은 텍스트가 포함 된 경우 변수로 교체하고자합니다. single quotes'double quotes"single quotesdouble quotes
  2. sed범위도 포함되어 $즉시와 같은 문자가옵니다 : $p, $d, $w. 당신이 그 탈출 그래서 그들은 또한, 확장 할 변수 모양을 $백 슬래시 [와 문자를 \같은] : \$p, \$d, \$w.

TERMINATE 전에 줄을 어떻게 가져 와서 다음 줄을 모두 삭제할 수 있습니까?
— Yugal Jindle

하드 코드 된 TERMINAL을 변수로 어떻게 대체 하시겠습니까?
— Sébastien Clément

2
여기서 누락 된 유스 케이스 중 하나는 마지막 마커 다음에 줄을 인쇄하는 방법입니다 (파일에 여러 줄이있을 수있는 경우 로그 파일 등 생각).
— mato

첫 번째 줄에서 예제 sed -e "1,/$matchtext/d"가 작동하지 않습니다 $matchtext. 로 변경해야했습니다 sed -e "0,/$matchtext/d".
— Karalga

61

간단한 근사치로 사용할 수 있습니다

grep -A100000 TERMINATE file

그 TERMINATE라인 다음에 최대 100000 라인 을 greps 하고 출력합니다.

맨 페이지에서

-A NUM, --after-context=NUM

줄을 일치시킨 후 후행 컨텍스트의 NUM 줄을 인쇄하십시오. 연속 된 일치 그룹 사이에 그룹 구분 기호 (-)를 포함하는 행을 배치합니다. -o 또는 --only-matching 옵션을 사용하면 효과가 없으며 경고가 표시됩니다.


그것은 효과가있을 수 있지만 많은 파일을 처리하려면 스크립트로 코딩해야합니다. 따라서 일반적인 해결책을 보여주십시오.
— Yugal Jindle

3
이것이 하나의 실용적인 해결책이라고 생각합니다!
— michelgotta

2
비슷하게 -B NUM, --before-context = NUM ​​행을 일치시키기 전에 선행 컨텍스트의 NUM 행을 인쇄합니다. 연속 된 일치 그룹 사이에 그룹 구분 기호 (-)를 포함하는 행을 배치합니다. -o 또는 --only-matching 옵션을 사용하면 효과가 없으며 경고가 표시됩니다.
— PiyusG

이 솔루션은 변수를 확인하기 위해 문자열로 쉽게 사용할 수 있기 때문에 저에게 효과적이었습니다.
— Jose Martinez

3
좋은 생각! 문맥의 크기에 대해 확신이 없다면 file대신 다음 과 같이 계산할 수 있습니다 .grep -A$(cat file | wc -l) TERMINATE file
— Lemming

26

여기에서 사용하는 도구는 awk입니다.

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

어떻게 작동합니까 :

  1. 'found'변수를 0으로 설정하여 false를 평가합니다.
  2. 'TERMINATE'와 일치하는 정규식이 있으면이를 1로 설정합니다.
  3. 'found'변수가 True로 평가되면 다음을 인쇄하십시오. :)

다른 솔루션은 매우 큰 파일에서 사용할 경우 많은 메모리를 소비 할 수 있습니다.


단순하고 우아하며 매우 일반적입니다. 내 경우는의 두 번째 발생 될 때까지 모든 인쇄 된 '###':cat file | awk 'BEGIN{ found=0} /###/{found=found+1} {if (found<2) print }'
— 알렉산더 Stelmaczonek

3
여기서 사용 하지 않는 도구 는 cat입니다. awk하나 이상의 파일 이름을 인수로 사용할 수 있습니다. 또한 참조 stackoverflow.com/questions/11710552/useless-use-of-cat
— tripleee

9

귀하의 질문을 올바르게 이해하면 줄 을 TERMINATE 포함하지 않고 뒤에 줄 을 원합니다 TERMINATE. awk간단한 방법 으로이 작업을 수행 할 수 있습니다.

awk '{if(found) print} /TERMINATE/{found=1}' your_file

설명:

  1. 모범 사례는 아니지만 모든 vars의 기본값이 0이거나 정의되지 않은 경우 빈 문자열이라는 사실에 의존 할 수 있습니다. 따라서 첫 번째 표현식 ( if(found) print)은 처음 부터 아무것도 인쇄하지 않습니다.
  2. 인쇄가 끝나면 이것이 시작 라인인지 확인하십시오 (포함되어서는 안됨).

이것은 줄 뒤 의 모든 줄 을 인쇄합니다 TERMINATE.


일반화:

  • 당신이 가진 파일이 시작 - 그리고 끝 -lines을 그리고 당신은 그 라인 사이의 라인 싶은 제외 시작 - 그리고 최종 -lines을.
  • 시작 줄 과 끝 줄은 줄과 일치하는 정규식으로 정의 할 수 있습니다.

예:

$ cat ex_file.txt 
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt 
A good line to include
And this line
Yep
$

설명:

  1. 최종 라인이 발견 되면 인쇄를 수행하지 않아야합니다. 이 검사는 결과에서 최종 행 을 제외 하기 위해 실제 인쇄 전에 수행됩니다 .
  2. found설정된 경우 현재 줄을 인쇄하십시오 .
  3. (가) 경우 시작 - 라인은 다음 설정 발견되어 found=1다음 줄이 인쇄되도록. 이 검사는 결과에서 시작 라인 을 제외하기 위해 실제 인쇄 후에 수행됩니다 .

노트:

  • 코드는 모든 awk-vars의 기본값이 0이거나 빈 문자열 (정의되지 않은 경우)이라는 사실에 의존합니다. 이것은 유효하지만 모범 사례가 아니기 때문에 BEGIN{found=0}awk- 표현의 시작 부분에 a 를 추가 할 수 있습니다 .
  • 여러 개의 시작-끝 블록이 발견되면 모두 인쇄됩니다.

1
대단한 예입니다. csplit, sed 및 복잡한 awk 명령의 모든 방식을 살펴 보는 데 2 ​​시간이 걸렸습니다. 이것은 내가 원하는 것을 할뿐만 아니라 내가 필요로하는 몇 가지 다른 관련 작업을 수행하도록 수정하는 방법을 유추 할 수있을 정도로 단순 해졌습니다. awk가 위대하고 쓰레기의 엉망이 아니라는 것을 기억합니다. 감사.
— user1169420

{if(found) print}awk에서 약간의 반 패턴입니다. 블록을 그냥 found또는 found;나중에 다른 필터가 필요한 경우 교체하는 것이 관용적 입니다.
— user000001

@ user000001 설명 해주십시오. 무엇을 어떻게 교체해야하는지 이해하지 못합니다. 어쨌든 나는 그것이 쓰여진 방식으로 무슨 일이 일어나고 있는지 분명하게 생각합니다.
— UlfR

1
당신은 대체 할 것이다 awk '{if(found) print} /TERMINATE/{found=1}' your_file함께 awk 'found; /TERMINATE/{found=1}' your_file그들이 모두 같은 일을한다.
— user000001

7

다음과 같이 bash 매개 변수 확장을 사용하십시오.

content=$(cat file)
echo "${content#*TERMINATE}"

당신은 무엇을 설명 할 수 있습니까?
— Yugal Jindle

"file"의 내용을 $ content 변수에 복사했습니다. 그런 다음 "TERMINATE"가 표시 될 때까지 모든 문자를 제거했습니다. 욕심 매칭을 사용하지 않았지만 $ {content ## * TERMINATE}로 욕심 매칭을 사용할 수 있습니다.
— Mu Qiao

다음은 bash 매뉴얼의 링크입니다. gnu.org/software/bash/manual/…
— Mu Qiao

6
파일 크기가 100GB 인 경우 어떻게됩니까?
— Znik

1
Downvote :이 끔찍한이다 (변수에 파일을 읽는) 잘못된 (를 인용하지 않고 변수를 사용하여, 당신은 제대로 사용해야합니다 printf또는 당신이 당신이 통과 정확히 알고 있어야합니다 echo.).
— tripleee

6

grep -10000000 'TERMINATE'파일

  • 정말 큰 파일을 작업하는 sed보다 훨씬 빠릅니다. 그것은 최대 10M 라인 (또는 당신이 넣은 것)까지 작동하므로, 당신이 치는 모든 것을 처리 할 수있을만큼 크게 만들지 마십시오.

4

sed또는 로 여러 가지 방법이 있습니다 awk.

sed -n '/TERMINATE/,$p' file

TERMINATE파일에서 찾은 다음 해당 줄에서 파일 끝까지 인쇄합니다.

awk '/TERMINATE/,0' file

이것은와 정확히 동일한 동작 sed입니다.

인쇄를 시작할 행 번호를 알고있는 경우 다음과 함께 지정할 수 있습니다 NR(레코드 수, 결국 행 번호를 나타냄).

awk 'NR>=535' file

예

$ seq 10 > a        #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10

당신은 또한 사용할 수 있습니다more +7 file
— 123

여기에는 일치하는 줄이 포함되며이 질문에서는 원하지 않습니다.
— mivk

@ mivk 글쎄, 이것은 또한 받아 들여진 대답과 두 번째로 많이 찬성 한 경우이므로 제목이 잘못되어 문제가있을 수 있습니다.
— fedorqui 'SO 중지 피해'

3

어떤 이유로 든 sed를 사용하지 않으려면 다음은 TERMINATE파일 끝까지 줄 일치를 인쇄 합니다.

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

다음은 TERMINATE파일 끝까지 다음 줄 일치에서 인쇄 됩니다.

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

하나의 프로세스에서 sed가 수행 할 수있는 작업을 수행하는 데 2 ​​개의 프로세스가 필요하며 grep과 tail 실행 사이에서 파일이 변경되면 결과가 일치하지 않을 수 있으므로 sed를 사용하는 것이 좋습니다. 또한 파일에 포함되지 않은 TERMINATE경우 첫 번째 명령이 실패합니다.


파일이 두 번 스캔됩니다. 100GB 크기라면 어떻게 되나요?
— Znik

1
이것이 답답한 해결책이기 때문에 하향 투표되었지만 대답의 90 %가 경고이기 때문에 상향 투표되었습니다.
— Mad Physicist


0

이것은 한 가지 방법이 될 수 있습니다. 파일의 어떤 줄에 grep 단어가 있고 파일에 몇 줄이 있는지 아는 경우 :

grep -A466 'TERMINATE'파일


1
줄 번호를 알면 grep필요하지 않습니다. 당신은 그냥 사용할 수 tail -n $NUM있으므로 이것은 실제로 대답이 아닙니다.
— Samveen

-1

sed는 작업에 훨씬 유용한 도구입니다. sed -n '/ re /, $ p'file

여기서 re는 regexp입니다.

또 다른 옵션은 grep의 --after-context 플래그입니다. 파일에서 wc를 사용하여 종료 할 올바른 값을 제공해야합니다. 이것을 -n과 일치 표현식과 결합하십시오.


--after-context는 괜찮지 만 모든 경우에 해당되는 것은 아닙니다.
— Yugal Jindle

당신은 다른 것을 제안 할 수 있습니까 .. ??
— Yugal Jindle

-2

마지막으로 찾은 행 "TERMINATE"에서 파일 끝까지 모든 행을 인쇄합니다.

LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG|tail -n 1|sed "s/:/ \\'/g"|awk -F" " '{print $1}'`
tail -n +$LINE_NUMBER $YOUR_FILE_NAME

grep공급할 수 있도록 라인 번호를 추출하는 tail것은 낭비적인 반 패턴입니다. 일치하는 파일을 찾고 파일의 끝까지 인쇄 (또는 반대로 첫 번째 일치하는 인쇄 및 중지)는 정규적이고 필수적인 정규식 도구 자체를 통해 수행됩니다. 대규모 grep | tail | sed | awk는 또한 그 자체로 막대한 쓸모없는 사용 grep과 친구 입니다.
— tripleee

나는 'TERMINATE'의 / last instance /를 찾고 그 인스턴스의 줄을 줄 무언가를 우리에게 주려고 노력했다고 생각합니다. 다른 구현은 첫 번째 인스턴스를 제공합니다. LINE_NUMBER는 다음과 같이 보일 것입니다. LINE_NUMBER = $ (grep -o -n 'TERMINATE'$ OSCAM_LOG | tail -n 1 | awk -F : '{print $ 1}') 가장 우아한 방법은 아니지만 일을 끝내는 것 같습니다. ^. ^
— fbicknel

... 또는 모두 한 줄에 있지만 추악한 경우 : tail -n + $ (grep -o -n 'TERMINATE'$ YOUR_FILE_NAME | tail -n 1 | awk -F : '{print $ 1}') $ YOUR_FILE_NAME
— fbicknel

.... 돌아가서 $ YOUR_FILE_NAME 대신 $ OSCAM_LOG를 편집하려고했지만 어떤 이유로 든 할 수 없습니다. $ OSCAM_LOG의 출처는 모릅니다. 난 그냥 무의식적으로 앵무새. oO
— fbicknel

Awk 101에서이 작업을 수행하는 것은 Awk 101의 일반적인 작업입니다. 이미 줄 번호를 얻기 위해 더 유능한 도구를 사용하고 있다면 더 유능한 도구 tail에서 작업을 수행하십시오. 어쨌든 제목에는 "첫 번째 일치"라고 명확하게 표시되어 있습니다.
— tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.