sed: 한 줄에서 여러 번 발생하는 패턴에서 텍스트를 추출합니다.

sed: 한 줄에서 여러 번 발생하는 패턴에서 텍스트를 추출합니다.

각 줄에 1~n번 나타날 수 있는 문자열 부분을 추출해야 합니다.

예를 들어, 이것은 나에게 필요한 것을 반영합니다.

This [dbo].[something] is a text containing [dbo].[something_else], then okay?
And then, [dbo].[something] may appear just once.
But why, nothing prevents [dbo].[something] from appearing twice as [dbo].[something] here.
And then can be three times, as [dbo].[something] is [dbo].[anything] but [dbo].[elsewhere] here.
[dbo].[otherthing] depicts another scenario with just one and pattern heading line
Or, also [dbo].[ultra] with an arbitrary amount of [dbo].[references] but ending with [dbo].[pattern]

\[dbo\]\.\[[^]]+\]예를 들어, 위의 텍스트에서 다음과 같은 결과를 원합니다.

something something_else
something
something something
something anything elsewhere
otherthing
ultra references pattern

그런 다음 모든 것을 인라인하거나 bash 배열에 추가하고 중복 항목을 필터링할 수 있습니다. 이는 문제가 되지 않습니다. 한 번의 스캔으로 이 필터를 수행하는 방법을 파악하는 데 문제가 있습니다.

여기서 내가 얻은 것은 마지막 일치 항목만 추출하는 것입니다(패턴 일치에 대한 sed의 "탐욕스러운" 접근 방식에 익숙해지면 이것이 왜 그런 경우인지 분명합니다).

cat dborefs.txt | sed -E "s/(.*\[dbo\]\.\[([^]]+)\].*)*/\2/g"
something_else
something
something
elsewhere
otherthing
pattern

패턴을 추출하고 교체하여 더 이상 일치하지 않게 하고 더 이상 일치하지 않을 때까지 다시 추출할 수 있지만 모든 bash 오버헤드를 고려하면 너무 많은 문제처럼 들립니다. 모든 것을 하나로 추출할 수 있으면 좋을 것입니다. 부르다 sed. 이것이 가능해야 한다고 생각하는데, 어떻게 해야 할지 쉽게 알 수 없습니다. 저는 이것이 다른 사람들에게 유용할 것이라고 생각했기 때문에 커뮤니티에서 이 질문을 여기서 공유하는 것이 유익할 것이라고 느꼈습니다.

답변1

개행으로 구분된 토큰 문자열 목록을 얻으려면 다음을 수행하십시오.

$ grep -o '\[dbo\]\.\[[^]]*\]' file | cut -d . -f 2 | tr -d '[]'
something
something_else
something
something
something
something
anything
elsewhere
otherthing
ultra
references
pattern

첫 번째는 grep으로 비트를 생성 [dbo].[word]하고 그 합계를 제거 cut합니다 .[word]tr[]

토큰 문자열을 발생하는 행별로 그룹화하려면 다음을 수행하십시오.

$ sed -e 's/\][^.[]*\[/] [/g' -e 's/^[^[]*//' -e 's/[^]]*$//' -e 's/\[dbo\]\.\[\([^]]*\)\]/\1/g' file
something something_else
something
something something
something anything elsewhere
otherthing
ultra references pattern

여기에 사용된 네 가지 대체는 다음과 같습니다.

  1. ]점이나 기호가 아닌 사이의 [모든 항목을 제거합니다 [(실제로는 공백으로 바꿉니다. 이는 최종 출력의 공백입니다).
  2. 첫 번째 것 이전의 모든 것을 삭제하십시오 [.
  3. 마지막 것 이후의 모든 것을 삭제하십시오 ].
  4. 나머지 콘텐츠에서 태그된 단어를 추출합니다.

답변2

현재, sed를 반복적으로 호출하는 것보다 더 나은 방법은 파일에 나타나지 않을 자리 표시자 "링크"로 바꾸는 것입니다.

cat dborefs.txt | sed -E "
 s/\[dbo\]\.\[([^]]+)\]/_-\1-_/g;
 s/(^|-_)([^_]+|_[^-])*(\$|_-)/ /g;
 s/(^ +| +\$)//g"

다시 말해서:

  • 먼저 모든 것을 [dbo].[<extract>]얻고 _-<extract>-_;
  • 그런 다음 마지막 텍스트 앞 _-, 사이, -_뒤 의 모든 텍스트를 단일 공백 ​​문자로 바꿉니다 ._--_
  • 그런 다음 각 줄의 시작과 끝 부분에 있는 공백 문자를 정리합니다.

이렇게 하면 원하는 결과를 얻을 수 있습니다. 이를 모두 배열로 연결한 다음 sort고유한 항목을 필터링할 수 있습니다. 하지만 나는 여전히 명령을 연결하지 않는 더 나은 방법이 있어야 한다고 생각합니다 sed.

답변3

일치 항목을 고유화하기 위해 해시(연관 배열)를 사용하면 Perl에서 이 작업을 더 쉽게 수행할 수 있습니다.

$ perl -nE 'while ($_ =~ /\[dbo\]\.\[(.*?)\]/g) {$h{$1}++} }{ for $k (keys %h) {say $k}' dborefs.txt 
otherthing
anything
elsewhere
something
pattern
something_else
ultra
references

이 함수를 반복적으로 적용하면 GNU Awk에서도 유사한 접근 방식이 가능합니다 match.

$ gawk '{
    while (match($0,/\[dbo\]\.\[([^]]+)\]/,a)) {h[a[1]]++; $0 = substr($0,RSTART+RLENGTH)}
  } 
  END{
    for (k in h) print k
  }' dborefs.txt 
references
elsewhere
something
something_else
pattern
otherthing
anything
ultra

캡처 그룹 배열을 제공하지 않는 기능을 가진 다른 Awk 구현의 경우 match일치 항목을 잘라야 합니다.

while (match($0,/\[dbo\]\.\[([^]]+)\]/)) {h[substr($0,RSTART+7,RLENGTH-8)]++; $0 = substr($0,RSTART+RLENGTH)}

답변4

이번에는 여러 유틸리티를 사용하는 또 다른 방법이 있습니다. 파이프라인의 sed 부분은 패턴을 추출하고, awk 부분은 첫 번째 발생 순서를 유지하면서 패턴을 고유하게 지정합니다.

sed -Ee '
  /\n/{P;D;}
  s/\[dbo]\.\[([^]]+)]/\n\1\n/;D
' dborefs.txt | awk '!a[$0]++'

관련 정보