grep, regex 또는 perl을 사용하여 패턴에 따라 문자열을 추출하는 방법


90

다음과 같은 파일이 있습니다.

    <table name="content_analyzer" primary-key="id">
      <type="global" />
    </table>
    <table name="content_analyzer2" primary-key="id">
      <type="global" />
    </table>
    <table name="content_analyzer_items" primary-key="id">
      <type="global" />
    </table>

나는 따라 따옴표 안에 아무것도 추출해야 name=즉,, content_analyzer, content_analyzer2content_analyzer_items.

Linux 상자에서이 작업을 수행하고 있으므로 sed, perl, grep 또는 bash를 사용하는 솔루션이 좋습니다.


5
부끄러워 할 필요가 없습니다. 환영합니다!
Benoit

8
나는 그것이 링크에 잘못하지 될 것이라고 느낄 stackoverflow.com/questions/1732348/...
크리스토퍼 Hammarström

유용한 의견에 감사드립니다. XML 형식이 제대로 지정되지 않은 점 사과드립니다. 단순화를 위해 일부 태그를 삭제했습니다.
랭글러

답변:


168

결과에 포함하지 않고 콘텐츠를 일치시켜야하므로 (일치해야 name=" 하지만 원하는 결과의 일부가 아님) 어떤 형태의 제로 너비 일치 또는 그룹 캡처가 필요합니다. 다음 도구를 사용하여 쉽게 수행 할 수 있습니다.

Perl

Perl을 사용하면 n 옵션을 사용하여 한 줄씩 반복하고 일치하는 경우 캡처 그룹의 내용을 인쇄 할 수 있습니다 .

perl -ne 'print "$1\n" if /name="(.*?)"/' filename

GNU grep

GNU grep과 같은 향상된 버전의 grep이있는 경우 -P옵션을 사용할 수 있습니다. 이 옵션은 Perl과 유사한 정규식을 활성화 \K하여 단축형 lookbehind 를 사용할 수 있습니다 . 일치 위치를 재설정하므로 앞의 모든 것은 너비가 0입니다.

grep -Po 'name="\K.*?(?=")' filename

o 옵션은 grep이 전체 행 대신 일치하는 텍스트 만 인쇄하도록합니다.

Vim-텍스트 편집기

또 다른 방법은 텍스트 편집기를 직접 사용하는 것입니다. Vim을 사용하여이를 수행하는 다양한 방법 중 하나는 줄없이 줄을 삭제 name=한 다음 결과 줄에서 내용을 추출하는 것입니다.

:v/.*name="\v([^"]+).*/d|%s//\1

표준 grep

이러한 도구에 액세스 할 수없는 경우 어떤 이유로 표준 grep을 사용하여 유사한 작업을 수행 할 수 있습니다. 그러나 주위를 둘러 보지 않으면 나중에 정리가 필요합니다.

grep -o 'name="[^"]*"' filename

결과 저장에 대한 참고 사항

위의 모든 명령에서 결과는로 전송됩니다 stdout. 다음을 추가하여 파일에 파이핑하여 언제든지 저장할 수 있음을 기억하는 것이 중요합니다.

> result

명령의 끝까지.


12
둘러보기 (GNU grep) :grep -Po '.*name="\K.*?(?=".*)'
추후 공지가있을 때까지 일시 중지되었습니다.

@Dennis Williamson, 좋습니다. 그에 따라 답변을 업데이트했지만 둘 다 .*제쳐두고 나에게 화를 내지 않기를 바랍니다. 질문하고 싶은데요, "제외 "" 보다 탐욕스럽지 않은 경기에서 어떤 이점이 있습니까? 이것을 싸움으로 받아들이지 마십시오. 저는 단지 호기심이 많고 정규식 전문가가 아닙니다. 또한 \K팁, 정말 좋습니다. 감사합니다 Dennis.
sidyll

2
내가 왜 화를 낼까요? 없이 .*, 할 수 있습니다 grep -Po '(?<=name=").*?(?=")'. 는 \K속기로 사용할 수 있지만 왼쪽 일치가 가변 길이 인 경우에만 실제로 필요합니다. 이와 같은 경우 둘러보기를 사용하는 이유는 상당히 분명합니다. 탐욕스럽지 않은 작업은 좀 더 깔끔해 보입니다 ( [^"]*.*?그리고 앵커 캐릭터를 반복 할 필요가 없습니다. 속도에 대해서는 모르겠습니다. 상황에 따라 많이 달라집니다. 도움이
되었으면

@Dennis Williamson : 확실히 여기에 많은 유용한 정보가 있습니다. 나는 \K그것을 (조사 후) 유지 하고 제거 한 이유 .*는 동일 하다고 생각합니다 . 그리고 .*?어딘가에서 배운 "전통적인 방법"대신 사용 하는 것을 생각해 본 적이 없습니다 . 그러나 여기에서 탐욕스럽지 않은 것은 정말로 의미가 있습니다. 감사합니다 Dennis, 최고의 소원.
sidyll

+1은 명령을 설명합니다. 정규식의 "[...]"부분을 설명하기 위해 답변을 업데이트 할 수 있다면 감사하겠습니다.
lreeder 2014 년


5

Perl을 사용하는 경우 XML :: Simple , XML :: Twig 또는 XML :: LibXML 을 구문 분석하는 모듈을 다운로드하십시오 . 바퀴를 재발 명하지 마십시오.


3
OP가 제공 한 예제는 형식이 올바르지 않으므로 ( <type="global"예 :) 대부분의 XML 파서가 불평하고 죽습니다.
bvr

5

이 목적을 위해서는 정규식보다는 HTML 파서를 사용해야합니다. 다음을 사용하는 Perl 프로그램 HTML::TreeBuilder:

프로그램

#!/usr/bin/env perl

use strict;
use warnings;

use HTML::TreeBuilder;

my $tree = HTML::TreeBuilder->new_from_file( \*DATA );
my @elements = $tree->look_down(
    sub { defined $_[0]->attr('name') }
);

for (@elements) {
    print $_->attr('name'), "\n";
}

__DATA__
<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>

산출

content_analyzer
content_analyzer2
content_analyzer_items

2

이것은 그것을 할 수 있습니다 :

perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'

2

HTML tidy 및 xmlstarlet을 사용하는 솔루션은 다음과 같습니다.

htmlstr='
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
'

echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null |
sed '/type="global"/d' |
xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n

1

죄송합니다. sed 명령은 물론 tidy 명령보다 우선해야합니다.

echo "$htmlstr" | 
sed '/type="global"/d' |
tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null |
xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n

0

xml (또는 일반적으로 텍스트)의 구조가 고정 된 경우 가장 쉬운 방법은 cut. 특정 사례의 경우 :

echo '<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>' | grep name= | cut -f2 -d '"'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.