sed로 캡처 한 그룹 만 출력하는 방법은 무엇입니까?


277

sed캡처 된 그룹 만 출력 할 수있는 방법이 있습니까? 예를 들어 입력이 주어진 경우 :

This is a sample 123 text and some 987 numbers

그리고 패턴 :

/([\d]+)/

역 참조로 형식이 지정된 방식으로 123 및 987 출력 만 얻을 수 있습니까?


그룹 캡처에는 플래그를 sed사용하여 확장 정규식을 설정 해야 합니다 -E.
peterh-

답변:


333

이것을 작동시키는 열쇠는 sed출력하고 싶지 않은 것을 제외하고 원하는 것을 지정하는 것입니다.

string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

이것은 말합니다 :

  • 각 줄을 인쇄하도록 기본 설정하지 마십시오 ( -n)
  • 0 개 이상의 숫자가 아닌 숫자를 제외
  • 하나 이상의 숫자를 포함
  • 하나 이상의 비 숫자를 제외
  • 하나 이상의 숫자를 포함
  • 0 개 이상의 숫자가 아닌 숫자를 제외
  • 치환을 인쇄하십시오 ( p)

일반적으로 sed괄호를 사용하여 그룹을 캡처하고 역 참조를 사용하여 캡처 한 내용을 출력합니다.

echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'

"bar"를 출력합니다. 확장 정규식에 -r( -EOS X의 경우)를 사용하는 경우 괄호를 이스케이프 처리하지 않아도됩니다.

echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'

최대 9 개의 캡처 그룹과 역 참조가있을 수 있습니다. 역 참조는 그룹이 나타나는 순서대로 번호가 매겨 지지만 어떤 순서로든 사용할 수 있으며 반복 될 수 있습니다.

echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'

"바 a"를 출력합니다.

GNU가있는 경우 grep(OS X를 포함하여 BSD에서도 작동 할 수 있음) :

echo "$string" | grep -Po '\d+'

또는 다음과 같은 변형 :

echo "$string" | grep -Po '(?<=\D )(\d+)'

-P옵션은 Perl 호환 정규 표현식을 활성화합니다. man 3 pcrepattern또는을 참조하십시오 man 3 pcresyntax.


24
참고로 OSX Mountain Lion은 더 이상 grep에서 PCRE를 지원하지 않습니다.
yincrash

1
참고로 grep -o 옵션은 Solaris 9에서 지원되지 않습니다. 또한 Solaris 9는 sed -r 옵션을 지원하지 않습니다. :(
Daniel Kats

7
sysadmin에게 gsed를 설치하도록 요청하십시오. 당신은 몇 가지 도넛이 당신을 얻을 것이다에 놀랄 것입니다 ...
avgvstvs

3
'('및 ')'앞에 '\'를 접두사로 사용해야 할 수도 있습니다. 왜 그런지 모르겠습니다.
lumbric

7
@lumbric : sed예제를 참조하는 경우 -r옵션 (또는 -EOS X, IIRC)을 사용하는 경우 괄호를 이스케이프 할 필요가 없습니다. 차이점은 기본 정규 표현식과 확장 정규 표현식 ( -r) 의 차이점입니다 .
추후 공지가있을 때까지 일시 중지되었습니다.

55

Sed에는 최대 9 개의 기억 패턴이 있지만 이스케이프 처리 된 괄호를 사용하여 정규 표현식의 일부를 기억해야합니다.

예와 자세한 내용은 여기 를 참조 하십시오


58
sed -e 's/version=\(.+\)/\1/' input.txt이것은 여전히 ​​전체 input.txt를 출력합니다
Pablo

@Pablo, 패턴에서 \+대신 작성해야합니다 +. 그리고 사람들이 왜 -e단 하나의 sed 명령을 사용하는지 이해하지 못합니다 .
프레드릭 가우스

1
다음을 sed -e -n 's/version=\(.+\)/\1/p' input.txt참조하십시오 : mikeplate.com/2012/05/09/…
awattar

1
나는 sed -EPerl / Java / JavaScript / Go / whatever에 가장 가까운 소위 "현대"또는 "확장 된"정규 표현식을 사용하는 것이 좋습니다 . (에 비교 grep -Eegrep.) 기본 구문은 그 이상한 탈출 규칙을 가지고 있으며, "쓸모없는"것으로 간주됩니다. 이 둘의 차이점에 대한 자세한 내용을 보려면을 실행하십시오 man 7 re_format.
AndrewF

31

당신은 grep을 사용할 수 있습니다

grep -Eow "[0-9]+" file

4
@ ghostdog74 : 당신과 동의합니다. greo가 캡처 된 그룹 만 출력하도록하려면 어떻게해야합니까?
Pablo

1
@Michael- o옵션이있는 이유 -unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only-matching 일치하는 행의 일부만 패턴
Bert F

14
@ Bert F : 일치하는 부분을 이해하지만 그룹을 캡처하지 않습니다. 내가 원하는 것은이 ([0-9] +). + ([abc] {2,3})을 갖도록하는 것입니다. 그래서 2 개의 캡처 그룹이 있습니다. 역 참조 또는 다른 방법으로 만 캡처 그룹을 출력하고 싶습니다.
Pablo

마이클 안녕하세요. grep으로 n 번째 캡처 그룹을 추출 했습니까?
doc_id

1
@Pablo : grep은 일치하는 것을 출력합니다. 여러 그룹을 제공하려면 여러 표현식을 사용하십시오. grep -Eow -e "[0-9]+" -e "[abc]{2,3}"이 두 표현식이 이전 grep에서 파이핑을 제외하고 한 줄에 있어야하는 방법을 모르겠습니다 (한 패턴이 한 줄에서 두 번 이상 일치하면 여전히 작동하지 않습니다) ).
idbrii

13

자릿수

이 답변은 모든 숫자 그룹에서 작동합니다. 예:

$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166

확장 된 답변.

sed에게 캡처 된 그룹 만 출력하도록 지시 할 수있는 방법이 있습니까?

예. 캡처 그룹으로 모든 텍스트를 대체하십시오.

$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123

s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.

또는 확장 된 구문 (역 따옴표가 적고 + 사용 가능) :

$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123

숫자가 없을 때 원본 텍스트를 인쇄하지 않으려면 다음을 사용하십시오.

$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
  • (-n) 기본적으로 입력을 인쇄하지 마십시오.
  • (/ p) 교체가 완료된 경우에만 인쇄하십시오.

그리고 여러 숫자를 일치시키고 인쇄하십시오.

$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456

그것은 모든 자릿수 실행에 효과적입니다.

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166

grep 명령과 매우 유사합니다.

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166

\ d에 대해

그리고 패턴 : /([\d]+)/

Sed는 '\ d'(바로 가기) 구문을 인식하지 못합니다. 위에서 사용 된 ascii 등가 [0-9]는 정확히 동일하지 않습니다. 유일한 대안은 '[[: digit :]]`문자 클래스를 사용하는 것입니다.

선택한 답변은 이러한 "문자 클래스"를 사용하여 솔루션을 빌드합니다.

$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

이 솔루션은 (정확히) 두 자리 숫자 만 작동합니다.

물론, 셸 내에서 답변이 실행됨에 따라 이러한 답변을 더 짧게하기 위해 몇 가지 변수를 정의 할 수 있습니다.

$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"

그러나 이미 설명했듯이 s/…/…/gp명령을 사용하는 것이 좋습니다.

$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987

그것은 반복되는 자릿수와 짧은 명령을 쓰는 것을 다룰 것입니다.


높은 투표 응답을 읽은 후 놀랐습니다. 좁은 범위에 대해 쓰고 실제로 질문의 정신을 다루기 위해 아래로 스크롤했습니다. 나는 누군가가 이미 몇 년 전에 그것을했을 것이라고 추측했을 것입니다. 이것은 잘 설명되어 있으며 정답입니다.
Amit Naidu

9

질문에 주어진 패턴은 단지 예일 뿐이며 목표는 모든 패턴 과 일치 하는 것이라고 생각합니다 .

패턴 공간에 줄 바꿈을 삽입 할 수있는 GNU 확장 이있는 sed 가있는 경우 한 가지 제안은 다음과 같습니다.

> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers

이 예제는 CYGWIN 과 함께 tcsh (예, 잘못된 쉘을 알고 있습니다 )와 함께 있습니다. (편집 : bash의 경우 세트를 제거하고 = 주위의 공백을 제거하십시오.)


@Joseph : 그러나 고맙지 만, 내 작업을 바탕으로 ghostdog74가 제안한 것처럼 grep이 더 자연스러운 것처럼 느낍니다. grep 출력을 캡처 그룹으로 만 만드는 방법 만 알아 내고 일치하는 것은 아닙니다.
Pablo

2
참고 사항이지만 더하기 기호 '+'는 '하나 이상'을 의미하므로 패턴을 반복 할 필요가 없습니다. 따라서 "[0-9] [0-9] *"는 "[0-9] +"가됩니다.
RandomInsano

4
@RandomInsano :를 사용하려면 +이스케이프를 이스케이프 처리하거나 -r옵션 ( -EOS X의 경우)을 사용해야합니다 . 당신은 또한 사용할 수 있습니다 \{1,\}(또는 -r또는 -E이스케이프없이).
추후 공지가있을 때까지 일시 중지되었습니다.

9

포기하고 펄을 사용하십시오

때문에 sed그것을 잘라하지 않는, 그냥 그것이 적어도 펄 수건을 던져 사용할 수 있도록 LSB 동안 grepGNU 확장이되지 않습니다 :-)

  • 일치하는 그룹이나 룩업이 필요없는 전체 일치 부분을 인쇄하십시오.

    cat <<EOS | perl -lane 'print m/\d+/g'
    a1 b2
    a34 b56
    EOS

    산출:

    12
    3456
  • 라인 당 단일 일치, 종종 구조화 된 데이터 필드 :

    cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
    a1 b2
    a34 b56
    EOS

    산출:

    1
    34

    lookbehind와 함께 :

    cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
    a1 b2
    a34 b56
    EOS
  • 여러 필드 :

    cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
    a1 c0 b2 c0
    a34 c0 b56 c0
    EOS

    산출:

    1 2
    34 56
  • 라인 당 여러 개의 일치, 종종 구조화되지 않은 데이터 :

    cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
    a1 b2
    a34 b56 a78 b90
    EOS

    산출:

    1 
    34 78

    lookbehind와 함께 :

    cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
    a1 b2
    a34 b56 a78 b90
    EOS

    산출:

    1
    3478

1
"sed와 함께"라는 질문의 끝으로 무엇을 얻지 못했습니까?
Moonchild

@Moonchild Google 직원은 신경 쓰지 않습니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
나는 이것이 유용하다는 것을 알았다. 모든 명령 행 정규식 문제를 sed로 해결할 필요는 없습니다.
PPPaul

5

시험

sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

cygwin에서 이것을 얻었습니다.

$ (echo "asdf"; \
   echo "1234"; \
   echo "asdf1234adsf1234asdf"; \
   echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
  sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

1234
1234 1234
1 2 3 4 5 6 7 8 9
$

2

OP가 요청한 것 (그룹 캡처)은 아니지만 다음을 사용하여 숫자를 추출 할 수 있습니다.

S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'

다음을 제공합니다.

123
987
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.