grep / egrep를 오용하는 것 같습니다.

여러 줄의 문자열을 검색하려고했지만 찾고있는 항목이 일치해야한다는 것을 알고있는 동안 일치 항목을 찾을 수 없었습니다. 원래는 내 정규식이 잘못되었다고 생각했지만 결국에는 도구는 한 줄로 작동합니다 (또한 내 정규식이 너무 사소해서 문제가 될 수 없습니다).

그러면 여러 줄에서 패턴을 검색하는 데 어떤 도구를 사용할까요?

댓글

  • sed, awk 또는 grep을 사용한 여러 줄 패턴 일치
  • @CiroSantilli- 이 Q와 연결 한 Q가 중복 된 것 같지 않습니다. 다른 Q는 ‘ 여러 줄 패턴 일치를 수행하는 방법 (예 : 어떤 도구를 이 작업을 수행하는 데 사용) grep로이 작업을 수행하는 방법을 묻는 동안. 밀접하게 관련되어 있지만 IMO는 아닙니다.
  • @sim 이러한 경우는 결정하기 어렵습니다. 귀하의 요점을 알 수 있습니다.이 특별한 경우가 중복되기 때문에 더 낫다고 생각합니다. 사용자는 "grep"가 동사 “를 grep에 “로 제안하고 상위 답변이라고 말했습니다. 허용되는 경우를 포함하여 ‘ grep을 사용하지 마십시오.
  • 여기에 여러 줄 정규 표현식이 필요하다는 표시가 없습니다. 입력 데이터 및 예상 출력 데이터와 함께 실제 예와 이전 작업을 보여주는 것을 고려해보십시오.

답변

다음은 여러 줄에 걸쳐 grep와 유사한 동작을 제공하는 sed입니다.

sed -n "/foo/{:start /bar/!{N;b start};/your_regex/p}" your_file 

작동 방식

  • -n는 모든 행을 인쇄하는 기본 동작을 억제합니다.
  • /foo/{}foo와 일치하는 줄에 구불 구불 한 부분을 수행합니다. foo를 패턴의 시작 부분으로 바꿉니다.
  • :start는 정규식의 끝을 찾을 때까지 루프를 계속하는 데 도움이되는 분기 레이블입니다.
  • /bar/!{}는 squigglies에있는 항목을 실행합니다. bar와 일치하지 않는 줄. 패턴의 끝 부분을 포함합니다.
  • N는 활성 버퍼에 다음 줄을 추가합니다 (sed는 이것을 패턴 공간이라고합니다.)
  • b start는 우리가 만든 start 라벨로 무조건 분기됩니다. 패턴 공간에 bar가 포함되지 않는 한 다음 줄을 계속 추가하기 위해 더 일찍.
  • /your_regex/pyour_regex와 일치하는 경우 패턴 공간을 인쇄합니다. your_regex를 여러 줄에 걸쳐 일치시키려는 전체 표현식으로 바꿔야합니다.

댓글

  • +1 이것을 toolikt에 추가하십시오! 감사합니다.
  • 참고 : MacOS에서는 sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • sed: unterminated { 오류 발생
  • li>

  • @Nomaed Shot in the dark here하지만 정규식에 ” {” 문자가 포함되어 있습니까? 그렇다면 ‘ 백 슬래시 이스케이프 처리해야합니다.
  • @Nomaed sed 구현 간의 차이점 위의 스크립트를 표준 규격으로 만들기 위해 해당 답변의 권장 사항을 따르려고했지만 ” start “가 정의되지 않았다고 말했습니다. 상표. 따라서 ‘이 작업이 표준 준수 방식으로 수행 될 수 있는지 확실하지 않습니다. 관리하는 경우 내 답변을 자유롭게 수정하십시오.

답변

일반적으로 도구를 사용합니다. pcregrep라고하며 yum 또는 apt를 사용하여 대부분의 Linux 버전에 설치할 수 있습니다.

예 :

콘텐츠가있는 testfile라는 파일이 있다고 가정합니다.

abc blah blah blah def blah blah blah 

다음 명령을 실행할 수 있습니다.

$ pcregrep -M "abc.*(\n|.)*def" testfile 

여러 줄에서 패턴 일치를 수행합니다.

또한, sed에서도 동일한 작업을 수행 할 수 있습니다.

$ sed -e "/abc/,/def/!d" testfile 

댓글

  • sed 제안 건너 뛰기 def가있는 줄

답변

간단히 Perl-regexp 매개 변수 P를 지원하는 일반 grep이이 작업을 수행합니다.

$ echo "abc blah blah blah def blah blah blah" | grep -oPz "(?s)abc.*?def" abc blah blah blah def 

(?s) DOTALL 수정자를 호출하여 정규식에서 점을 만들어 문자뿐만 아니라 줄 바꿈도 일치시킵니다.

댓글

  • 이 솔루션을 시도해도 출력이 ‘ def

    그러나 파일의 끝으로 이동 ‘ blah ‘

  • 아마도 귀하의 grep은 -P 옵션을 지원하지 않습니다.
  • 이것은 저에게 유일한 방법이었습니다. 모든 sed 제안을 시도했습니다. 하지만 ‘ grep 대안을 설치하는 데까지 이르지 않았습니다.
  • $ grep --version : grep (GNU grep) 3.1 Windows Git Bash 에는 -P, --perl-regexp 옵션이 있지만 (?s)에는 옵션이 없습니다 ‘가 작동하지 않는 것 같습니다. 여전히 첫 번째 줄만 표시됩니다. 동일한 테스트 문자열을 가진 동일한 패턴이 regex101.com 에서 작동합니다. Git Bash에 대안이 있습니까? sed? (sed (GNU sed) 4.8 여기)
  • 컨텍스트 를 출력에 추가하는 방법을 알고 있습니까? grep -1은 ‘ 여기서 작동하지 않습니다.

답변

“Perl을 사용한보다 간단한 접근 방식 :

perl -e "$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m" file 

또는 (JosephR sed 경로 , 뻔뻔하게 그의 제안 을 훔칩니다.

perl -n000e "print $& while /^foo.*\nbar.*\n/mg" file 

### Explanation

$f=join("",<>); : 전체 파일을 읽고 내용 (줄 바꿈 및 전체)을 변수

. 그런 다음 foo\nbar.*\n 일치를 시도하고 일치하면 인쇄합니다 (특수 변수 $&는 발견 된 마지막 일치 항목을 보유합니다). ///m는 줄 바꿈에서 정규식을 일치시키는 데 필요합니다.

-0는 입력 레코드 구분자를 설정합니다. 이것을 00로 설정하면 Perl이 연속적인 줄 바꿈 (\n\n)을 레코드 구분자로 사용하는 “단락 모드”가 활성화됩니다. 연속 된 줄 바꿈이없는 경우 전체 파일을 한 번에 읽습니다 (슬러 핑).

### 경고 : 대용량 파일에 대해이 작업을 수행하지 마세요 . 전체 파일을 메모리에 저장하면 문제가 될 수 있습니다.

댓글

  • 나는 ‘ Perl에 대해 많이 알고 있지만 ‘ 엄격히 말하자면 my $f=join("",<>); 일 필요는 없습니까?
  • @Sapphire_Brick 전용 엄격 모드 (use strict;) 인 경우. 특히 더 큰 스크립트를 작성할 때 ‘ 좋은 습관이지만, 이와 같이 작은 한 줄짜리에서는 ‘ 과도하게 사용됩니다. 하나.

답변

파일이 있다고 가정합니다. test.txt 포함 :

blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla 

다음 코드를 사용할 수 있습니다.

sed -n "/foo/,/bar/p" test.txt 

다음 출력의 경우 :

foo here is the text to keep between the 2 patterns bar 

답변

grep 대안 sift 는 여러 줄 일치를 지원합니다 (면책 조항 : 저자입니다).

testfile 포함 :

 <book> <title>Lorem Ipsum</title> <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</description> </book> 

sift -m '<description>.*?</description>' ( 설명)

결과 :

 testfile: <description>Lorem ipsum dolor sit amet, consectetur testfile: adipiscing elit, sed do eiusmod tempor incididunt ut testfile: labore et dolore magna aliqua</description> 

sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename (추출 및 설명 형식 변경)

결과 :

description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" 

댓글

  • 아주 멋진 도구입니다. 축하합니다! Ubuntu와 같은 배포판에 포함 해보십시오.

Answer

저는 grep을 사용하여이 문제를 해결했습니다. 다른 grep이있는 옵션.

grep first_line_word -A 1 testfile | grep second_line_word 

-A 1 옵션은 찾은 줄 뒤에 한 줄을 인쇄합니다. 물론 파일과 단어 조합에 따라 다릅니다. 하지만 저에게는 가장 빠르고 안정적인 솔루션이었습니다.

댓글

  • alias grepp = ‘ grep –color = auto -B10 -A20 -i ‘ 그런 다음 cat somefile | grepp blah | grepp foo | grepp bar … 네 -A와 -B는 매우 편리합니다 …가장 좋은 답변이 있습니다.
  • 이는 ‘ 초 결정적이지 않으며 다른 단일 라인을 얻기 위해 전체 패턴을 무시합니다. 첫 번째 줄에). ‘ 당신이 원하는 패턴에 도달하기 위해 필요한만큼 멀리 가도록 프로그램에 지시하는 것이 좋습니다. ‘ 일치시키려는 텍스트의 끝이 ‘ 확실합니다. 예를 들어 testfile가 업데이트되어 second_line_word가 세 번째 줄에있는 경우 이제 첫 번째 줄이 누락 된 것이 아닙니다. 두 번째 grep)이지만 ‘ 둘 사이에 나타나기 시작한 줄이 누락되지 않았습니다.
  • 이 이미 이해 한 출력에 한 줄만 표시하려는 ad hoc 명령에는 충분한 MO가 될 것입니다. 나는 ‘ ‘가 OP가 추구하는 것이라고 생각하지 않으며 아마도 그 시점에서 복사 / 붙여 넣기를 할 수도 있습니다. 임시적입니다.

답변

이 작업을 수행하는 한 가지 방법은 Perl을 사용하는 것입니다. 예 : 다음은 foo라는 파일의 내용입니다.

foo line 1 bar line 2 foo foo foo line 5 foo bar line 6 

자, 여기에 Perl이 있습니다. foo로 시작하고 다음에 bar로 시작하는 모든 줄과 일치 :

cat foo | perl -e "while(<>){$all .= $_} while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) { print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m; }" 

Perl, 세분화 :

  • while(<>){$all .= $_} 전체 표준 입력을 변수 $all
  • 변수 all에는 정규식이 있습니다 …
  • /^(foo[^\n]*\nbar[^\n]*\n)/m 정규식 : foo 줄의 시작 부분에 줄 바꿈이 아닌 문자, 줄 바꿈, 바로 “bar”, 그리고 줄이있는 나머지 줄. 정규식 끝에있는 /m는 “여러 줄에서 일치”를 의미합니다.
  • print $1 정규식의 일부를 인쇄합니다. 괄호 안에있는 (이 경우 전체 정규식)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m 정규식에 대한 첫 번째 일치 항목을 삭제하여 정규식의 여러 대소 문자를 일치시킬 수 있습니다. 문제의 파일

및 출력 :

foo line 1 bar line 2 foo bar line 6 

댓글

  • Perl을 좀 더 관용적으로 줄일 수 있다는 점을 알려드립니다. perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

답변

자신을 제외한 두 패턴 사이의 텍스트를 얻으려면

test.txt 포함 :

blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla 

다음 코드를 사용할 수 있습니다.

 sed -n "/foo/{ n b gotoloop :loop N :gotoloop /bar/!{ h b loop } /bar/{ g p } }" test.txt 

다음 출력의 경우 :

here is the text to keep between the 2 patterns 

어떻게 작동합니까? 단계별로 만들기

  1. /foo/{는 줄에 “foo”가 포함되어있을 때 트리거됩니다.
  2. n 패턴 공간을 다음 줄로 바꿉니다. 즉, “here”라는 단어가 “gotoloop”레이블로 분기됩니다.
  3. b gotoloop > :gotoloop는 “gotoloop”레이블을 정의합니다.
  4. /bar/!{ 패턴에 “bar”가 포함되지 않은 경우
  5. h 보류 공간을 패턴으로 바꾸면 “여기”가 보류 공간에 저장됩니다.
  6. b loop “루프”레이블에 대한 분기
  7. :loop는 “루프”레이블을 정의합니다.
  8. N 패턴을 보류 공간에 추가합니다.
    이제 보류 공간에 다음이 포함됩니다.
    “here”
    “is the”
  9. :gotoloop 이제 4 단계에 있습니다. 줄에 “bar”가 포함될 때까지 반복합니다.
  10. /bar/ 루프가 완료되고 “bar”가 발견되었습니다. s 패턴 공간
  11. 패턴 공간은 기본 루프 중에 저장 한”foo “와”bar “사이의 모든 행을 포함하는 보류 공간으로 대체됩니다.
  12. p 패턴 공간을 표준 출력으로 복사

완료!

댓글

  • 잘하셨습니다. +1. 저는 일반적으로 이러한 명령을 tr ‘ SOH로 줄 바꿈하고 일반 sed 명령을 수행 한 다음 줄 바꿈을 바꾸는 방식으로 사용하지 않습니다.

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다