sed, awk 또는 gawk를 사용하여 일치하는 항목 만 인쇄하는 방법은 무엇입니까?


100

sed, awk 또는 gawk를 사용하여 검색 및 바꾸기와 같은 작업을 수행하는 방법에 대한 많은 예제와 매뉴얼 페이지를 봅니다.

하지만 제 경우에는 특정 값을 추출하기 위해 텍스트 파일에 대해 실행하려는 정규식이 있습니다. 검색 및 바꾸기를 원하지 않습니다. 이것은 bash에서 호출됩니다. 예를 들어 보겠습니다.

정규 표현식의 예 :

.*abc([0-9]+)xyz.*

입력 파일 예 :

a
b
c
abc12345xyz
a
b
c

간단하게 들리지만 sed / awk / gawk를 올바르게 호출하는 방법을 알 수 없습니다. 내가 원하는 것은 bash 스크립트 내에서 다음과 같습니다.

myvalue=$( sed <...something...> input.txt )

내가 시도한 것은 다음과 같습니다.

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing

10
와 ... 사람들이이 질문을 -1로 투표 했나요? 정말 그렇게 부적절합니까?
Stéphane

Regex와 sed / awk와 같은 강력한 명령 줄 유틸리티 또는 vi, emacs 또는 teco와 같은 편집기를 사용하는 것은 단순히 일부 응용 프로그램을 사용하는 것보다 프로그래밍과 비슷할 수 있습니다. IMO 이것은 SU보다 더 많이 속합니다.
2009

아마도 초기 형식에서 요구 사항 중 일부를 명확하게 정의하지 않았기 때문에 투표에서 제외되었을 수 있습니다. 답변에 대한 OP의 의견을 읽지 않는 한 여전히 그렇지 않습니다 (일이 배 모양이 될 때 삭제 한 의견 포함).
pavium

답변:


42

sed(Mac OS X)가 +. *대신 시도 하고 p인쇄 일치 태그를 추가했습니다 .

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt

없이 하나 이상의 숫자를 일치 시키 +려면 다음을 사용합니다.

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt

고마워요, + 대신 *를 사용하면 나에게도 효과적이었습니다.
Stéphane

2
... 그리고 일치를 인쇄하는 "p"옵션도 나도 몰랐습니다. 다시 한 번 감사드립니다.
Stéphane

2
나는 탈출해야만했다. +그리고 그것은 나를 위해 일했다 :sed -n 's/^.*abc\([0-9]\+\)xyz.*$/\1/p'
추후 통지가있을 때까지 일시 중지.

3
최신 RE 형식을 사용하지 않기 때문에 +는 표준 문자이고 {,} 구문으로 표현해야합니다. use -E sed 옵션을 추가하여 최신 RE 형식을 트리거 할 수 있습니다. 확인 re_format (7), 설명의 특히 마지막 단락 developer.apple.com/library/mac/#documentation/Darwin/Reference/...
anddam

33

sed를 사용하여이 작업을 수행 할 수 있습니다.

 sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
  • -n 결과 라인을 인쇄하지 마십시오
  • -r이렇게하면 캡처 그룹 괄호를 탈출 할 수 없습니다 ().
  • \1 포획 그룹 경기
  • /g 글로벌 경기
  • /p 결과를 인쇄

이 작업을 더 쉽게 만들어주는 도구 를 직접 작성했습니다.

rip 'abc(\d+)xyz' '$1'

3
이것은 지금까지 가장 잘 설명 된 답변입니다!
Nik Reiman

몇 가지 설명을 통해 문제의 문제점을 이해하는 것이 더 좋습니다. 감사합니다 !
r4phG

17

나는 perl이것을 더 쉽게 만들기 위해 사용 합니다. 예 :

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'

이것은 Perl을 실행하고이 -n옵션은 Perl이 STDIN에서 한 번에 한 줄씩 읽고 코드를 실행하도록 지시합니다. 이 -e옵션은 실행할 명령을 지정합니다.

이 명령어는 read 행에서 regexp를 실행하고 일치하는 경우 첫 번째 중괄호 ( $1) 세트의 내용을 인쇄합니다 .

당신은 또한 끝에 여러 파일 이름을 할 수 있습니다. 예 :

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt


감사합니다.하지만 우리는 perl에 접근 할 수 없습니다. 이것이 제가 sed / awk / gawk에 대해 물어 본 이유입니다.
Stéphane

5

버전 경우 grep지원을 당신이 사용할 수있는 -o인쇄 옵션을 단지 당신의 정규 표현식 일치하는 모든 라인의 일부를.

그렇지 않다면 여기에 sed내가 생각 해낼 수 있는 최선의 방법이 있습니다 .

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

... 숫자없이 삭제 / 건너 뛰고 나머지 행의 경우 모든 선행 및 후행 숫자가 아닌 문자를 제거합니다. (나는 당신의 의도가 하나를 포함하는 각 줄에서 숫자를 추출하는 것이라고 추측하고 있습니다).

다음과 같은 문제 :

sed -e 's/.*\([0-9]*\).*/&/' 

.... 또는

sed -e 's/.*\([0-9]*\).*/\1/'

... sed"욕심 많은"일치 만 지원하므로 첫 번째. *가 나머지 줄과 일치합니다. 부정한 문자 클래스를 사용하여 탐욕스럽지 않은 일치를 달성하거나 sedPerl과 호환되는 버전 또는 정규식에 대한 다른 확장을 사용하지 않는 한 패턴 공간 (줄)에서 정확한 패턴 일치를 추출 할 수 없습니다. ).


다음 sed과 같은 방법으로 두 명령을 결합 할 수 있습니다 .sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'
추후 공지가있을 때까지 일시 중지되었습니다.

이전에는 grep의 -o 옵션에 대해 몰랐습니다. 알아서 반갑습니다. 그러나 "(...)"가 아닌 전체 일치를 인쇄합니다. 따라서 "abc ([[: digit :]] +) xyz"에서 일치하는 경우 "abc"및 "xyz"와 숫자를 얻습니다.
Stéphane

상기시켜 주셔서 감사합니다 grep -o! 나는 이것을 시도하고 sed일부 라인에서 여러 경기를 찾아야 할 필요성에 어려움을 겪었습니다. 내 솔루션입니다 stackoverflow.com/a/58308239/117471
브루노 Bronosky

3

awkwith match()를 사용하여 캡처 된 그룹에 액세스 할 수 있습니다 .

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file
12345

이것은 패턴을 일치 시키려고합니다 abc[0-9]+xyz. 그렇게하면 matches첫 번째 항목이 block 인 array에 슬라이스를 저장합니다 [0-9]+. match() 해당 하위 문자열이 시작되는 문자 위치 또는 인덱스를 반환 하므로 (문자열의 시작 부분에서 시작하는 경우 1)print 작업을 트리거합니다 .


으로 grep당신은 모양 숨김 및보기 미리 사용할 수 있습니다 :

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
12345

$ grep -oP 'abc\K[0-9]+(?=xyz)' file
12345

이 체크 패턴 [0-9]+이 내에서 발생 abc하고 xyz그냥 숫자를 인쇄합니다.


2

perl은 가장 깨끗한 구문이지만 perl이 없으면 (항상 그런 것은 아닙니다) 정규식의 gawk와 구성 요소를 사용하는 유일한 방법은 gensub 기능을 사용하는 것입니다.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file

샘플 입력 파일의 출력은

12345

참고 : gensub는 전체 정규식 (// 사이)을 대체하므로 대체에서 숫자 앞뒤의 텍스트를 제거하려면 ([0-9] +) 앞뒤에. *를 넣어야합니다.


2
gawk를 사용해야하거나 사용하려는 경우 영리하고 실행 가능한 솔루션입니다. 당신은 이것을 언급했지만 명확하게 : 비 GNU awk에는 gensub ()가 없으므로 이것을 지원하지 않습니다.
cincodenada

좋은! 그러나 match()캡처 된 그룹에 액세스하는 데 사용 하는 것이 가장 좋습니다 . 이것에 대한 내 대답 을 참조하십시오 .
fedorqui 'SO stop harming''16

1

라인을 선택하려면 원하지 않는 비트를 제거하십시오.

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'

기본적으로 원하는 줄을 egrep선택한 다음 sed숫자 앞뒤의 비트를 제거하는 데 사용 합니다.

여기에서 실제로 작동하는 것을 볼 수 있습니다.

pax> echo 'a
b
c
abc12345xyz
a
b
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//'
12345
pax> 

업데이트 : 분명히 실제 상황이 더 복잡하다면 RE를 수정해야합니다. 예를 들어 시작과 끝에서 항상 0 개 이상의 비 숫자 내에 단일 숫자가 묻혀있는 경우 :

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

흥미롭게도 ... 복잡한 정규식을 적용하고 (...) 섹션에있는 내용을 되 돌리는 간단한 방법이 없습니까? 왜냐하면 grep으로 먼저하고 sed로 여기서 한 일을 보는 동안 우리의 실제 상황은 "abc"와 "xyz"를 삭제하는 것보다 훨씬 더 복잡합니다. 추출하려는 텍스트의 양쪽에 많은 다른 텍스트가 나타날 수 있기 때문에 정규식이 사용됩니다.
Stéphane

RE가 정말 복잡한 경우 더 나은 방법 있다고 확신 합니다. 몇 가지 예나 더 자세한 설명을 제공했다면 이에 맞게 답변을 조정할 수 있습니다.
paxdiablo

0

OP의 경우는 한 줄에 여러 일치가있을 수 있음을 지정하지 않지만 Google 트래픽의 경우에도 이에 대한 예를 추가하겠습니다.

OP의 필요는 패턴에서 그룹을 추출하는 grep -o것이므로 사용 하려면 2 번의 패스가 필요합니다. 그러나 저는 이것이 작업을 완료하는 가장 직관적 인 방법이라고 생각합니다.

$ cat > example.txt <<TXT
a
b
c
abc12345xyz
a
abc23451xyz asdf abc34512xyz
c
TXT

$ cat example.txt | grep -oE 'abc([0-9]+)xyz'
abc12345xyz
abc23451xyz
abc34512xyz

$ cat example.txt | grep -oE 'abc([0-9]+)xyz' | grep -oE '[0-9]+'
12345
23451
34512

프로세서 시간은 기본적으로 무료이지만 인간의 가독성은 값을 매길 수 없기 때문에 "1 년 후, 이것이 무엇을한다고 생각할까요?"라는 질문에 따라 코드를 리팩토링하는 경향이 있습니다. 사실 공개적으로 공유하거나 팀과 공유하려는 코드의 man grep경우 긴 옵션이 무엇인지 파악하고이를 대체 할 수도 있습니다. 이렇게 :grep --only-matching --extended-regexp


-1

쉘로 할 수 있습니다

while read -r line
do
    case "$line" in
        *abc*[0-9]*xyz* ) 
            t="${line##abc}"
            echo "num is ${t%%xyz}";;
    esac
done <"file"

-3

awk. 다음 스크립트를 사용합니다.

/.*abc([0-9]+)xyz.*/ {
            print $0;
            next;
            }
            {
            /* default, do nothing */
            }

이것은 숫자 값을 출력하지 않으며 ([0-9+])전체 행을 출력합니다.
Mark Lakata 2013

-3
gawk '/.*abc([0-9]+)xyz.*/' file

2
이것은 작동하지 않는 것 같습니다. 일치하는 대신 전체 줄을 인쇄합니다.
Stéphane

샘플 입력 파일에서 해당 패턴은 전체 행입니다. 권리??? 패턴이 특정 필드에있을 것임을 알고 있다면 $ 1, $ 2 등을 사용하십시오. 예 : gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/'file
ghostdog74
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.