주변 문자를 인쇄하지 않고 "sed"와 일치하는 정규식 추출

주변 문자를 인쇄하지 않고 "sed"와 일치하는 정규식 추출

모든 "sed" 의사들에게:

한 줄에서 일치하는 정규식을 추출하기 위해 'sed'를 어떻게 얻나요?

즉, 정규식에 해당하는 문자열만 원하고 포함 줄에서 일치하지 않는 모든 문자를 제거합니다.

다음과 같이 역참조 기능을 사용해 보았습니다.

regular expression to be isolated 
         gets `inserted` 
              here     
               |
               v  
 sed -n 's/.*\( \).*/\1/p 

이는 다음과 같은 특정 표현에 적용됩니다.

 sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p 

"CONFIG_...."(일부 "*.h" 파일에 있음)로 시작하는 모든 매크로 이름을 깔끔하게 추출하여 한 줄씩 인쇄합니다.

          CONFIG_AT91_GPIO
          CONFIG_DRIVER_AT91EMAC
                   .
                   .   
          CONFIG_USB_ATMEL
          CONFIG_USB_OHCI_NEW
                   .
                 e.t.c. 

그러나 위의 내용은 다음과 같이 분류됩니다.

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

이는 항상 한 자리 숫자를 반환합니다.

                 7
                 9
                 .
                 .  
                 6

연속적인 숫자 필드를 추출하는 대신.

              8908078
              89670890  
                 .
                 .  
                 .
               23019   
                 .
               e.t.c.  

추신: "sed"에서 이를 구현하는 방법에 대한 피드백을 주시면 감사하겠습니다. 나는 "grep"과 "awk"를 사용하여 이 작업을 수행하는 방법을 알고 있습니다. "sed"에 대한 (제한적이긴 하지만) 이해에 구멍이 있는지 그리고 "sed"에서 이를 수행할 수 있는 방법이 있는지 궁금합니다. 한 가지 점 은
단순히 무시되었습니다.

답변1

정규식에 그룹이 포함된 경우 문자열을 일치시키는 방법이 여러 가지일 수 있습니다. 그룹이 포함된 정규식은 모호합니다. 예를 들어 정규식 ^.*\([0-9][0-9]*\)$과 문자열을 생각해 보세요 a12. 두 가지 가능성이 있습니다:

  • 대결과 대결 a은..*2[0-9]*1[0-9]
  • a1와 일치 하고 .*와 일치하는 빈 문자열 입니다 [0-9]*.2[0-9]

다른 모든 정규식 도구와 마찬가지로 Sed는 가장 빠른 일치 규칙을 적용합니다. 먼저 첫 번째 가변 길이 부분을 가능한 가장 긴 문자열과 일치시키려고 시도합니다. 문자열의 나머지 부분을 정규식의 나머지 부분과 일치시키는 방법을 찾으면 괜찮습니다. 그렇지 않으면 sed는 첫 번째 가변 길이 부분의 다음으로 가장 긴 일치를 시도하고 다시 시도합니다.

여기서는 가장 긴 문자열이 먼저 a1일치 .*하므로 그룹은 2. 일부 정규식 엔진을 .*사용하면 그룹을 더 일찍 시작하려는 경우 욕심을 덜 수 있지만 sed에는 그러한 기능이 없습니다. 그래서 당신은 필요명확성추가 앵커 포인트가 있습니다. 행간이 .*숫자로 끝날 수 없도록 지정하여 그룹의 첫 번째 숫자가 가능한 첫 번째 일치 항목이 되도록 지정합니다.

  • 숫자 세트가 줄의 시작 부분에 있을 수 없는 경우:

    sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
    
  • 숫자 그룹이 줄의 시작 부분에 있을 수 있고 sed가 \?선택적 부분 연산자를 지원하는 경우:

    sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
    
  • 숫자 그룹이 줄의 시작 부분에 있을 수 있는 경우 표준 정규식 구조를 따릅니다.

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

그런데 동일한 가장 빠른-긴 일치 규칙은 [0-9]*다음 숫자가 아닌 첫 번째 숫자 이후의 숫자를 일치 시킵니다 .*.

한 줄에 여러 개의 숫자 시퀀스가 ​​있는 경우 프로그램은 항상 마지막 숫자 시퀀스를 추출합니다. 이는 가장 먼저 가장 긴 일치 규칙이 처음에 적용되기 때문입니다 .*. 첫 번째 숫자 시퀀스를 추출하려면 이전 숫자 시퀀스를 지정해야 합니다. 숫자가 아닌 시퀀스입니다.

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

보다 일반적으로, 정규식의 첫 번째 항목을 추출하려면 해당 정규식의 부정을 계산해야 합니다. 이론적으로는 항상 가능하지만, 부정의 크기는 부정해야 하는 정규식의 크기에 따라 기하급수적으로 커지므로 이는 종종 비실용적입니다.

다른 예를 고려하십시오.

sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'

이 예는 실제로 동일한 문제를 나타내지만 일반적인 입력에서는 이를 볼 수 없습니다. 이를 입력하면 위의 명령이 대신 hello CONFIG_FOO_CONFIG_BAR인쇄합니다 .CONFIG_BARCONFIG_FOO_CONFIG_BAR

sed를 사용하여 첫 번째 일치 항목을 인쇄하는 방법이 있지만 약간 까다롭습니다.

sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p

(sed가 대체 텍스트 \n에서 개행 문자 표현을 지원한다고 가정합니다 s.) 이는 sed가 정규식과 가장 먼저 일치하는 항목을 찾고 CONFIG_…해당 비트 이전에는 어떤 항목도 일치시키려고 하지 않기 때문에 작동합니다. 줄 안에 개행 문자가 없기 때문에 이를 임시 표시로 사용할 수 있습니다. 이 명령은 이전 명령이 일치하지 않으면 T포기한다는 의미입니다 .s

sed에서 어떤 작업을 수행하는 방법을 알 수 없으면 awk로 전환하세요. 다음 명령은 정규식의 가장 빠른 일치 항목과 가장 긴 일치 항목을 인쇄합니다.

awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'

단순하게 유지하려면 Perl을 사용하십시오.

perl -l -ne '/[0-9]+/ && print $&'       # first match
perl -l -ne '/^.*([0-9]+)/ && print $1'  # last match

답변2

그렇지는 않지만 sed종종 간과되는 한 가지 사실은 grep -o제 생각에는 이것이 작업에 더 나은 도구라는 것입니다.

예를 들어, CONFIG_커널 구성에서 모든 매개변수를 가져오려면 다음을 사용할 수 있습니다.

# grep -Eo 'CONFIG_[A-Z0-9_]+' config
CONFIG_64BIT
CONFIG_X86_64
CONFIG_X86
CONFIG_INSTRUCTION_DECODER
CONFIG_OUTPUT_FORMAT

연속적인 숫자 시퀀스를 얻으려면 다음을 수행하십시오.

$ grep -Eo '[0-9]+' foo

답변3

sed '/\n/P;//!s/[0-9]\{1,\}/\n&\n/;D'

n... 오른쪽 대체 필드에서 s 대신 문자 그대로 개행 문자가 필요할 수도 있지만 문제 없이 이 작업을 수행합니다 . 그런데 .*CONFIG이 방법은 온라인에 일치하는 항목이 하나만 있는 경우에만 작동합니다. 그렇지 않으면 항상 마지막 일치 항목만 가져옵니다.

너는 볼 수있어이것작동 방식에 대한 설명이 있지만 이는 한 줄에서 발생하는 것과 동일한 수의 일치 항목만 별도의 줄에 인쇄합니다.

동일한 전략을 사용하여 [num]연속으로 번째 항목을 얻을 수 있습니다. 예를 들어, CONFIG 일치 항목만 인쇄하려는 경우(한 줄에서 세 번째 일치 항목인 경우에만):

sed '/\n/P;//d;s/CONFIG[[:alnum:]]*/\n&\n/3;D'

... CONFIG각 문자열이 영숫자가 아닌 문자 하나 이상으로 구분된다고 가정하더라도 말이죠.

제 생각에는 - 숫자의 경우 - 이것도 작동할 것 같습니다:

sed -n 's/[^0-9]\{1,\}/\n/g;s/\n*\(.*[0-9]\).*/\1/p

...오른손에 대해서는 이전과 동일한 경고입니다 \n. 이것은 첫 번째 것보다 훨씬 빠르지만 분명히 보편적으로 적용할 수는 없습니다.

CONFIG의 경우 P;...;D위의 루프를 패턴과 함께 사용하거나 다음과 같이 할 수 있습니다.

sed -n 's/[^C]*\(CONFIG[[:alnum:]]*\)\{0,1\}C\{0,1\}/\1\n/g;s/\(\n\)*/\1/g;/C/s/.$//p'

...이는 약간 더 복잡하며 sed올바르게 정렬된 참조 우선순위에 따라 작동합니다. 또한 한 줄의 모든 CONFIG 일치 항목을 한 번에 격리합니다. 비록 이전과 동일한 가정을 하지만 각 CONFIG 일치 항목은 영숫자가 아닌 문자 하나 이상으로 구분됩니다. GNU를 사용하면 다음과 sed같이 작성할 수 있습니다.

sed -En 's/[^C]*(CONFIG\w*)?C?/\1\n/g;s/(\n)*/\1/g;/C/s/.$//p'

관련 정보