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


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.