다른 명령에서 파이핑할 때 원시 패턴 공간을 일치하는 문자열로 제한합니다.

다른 명령에서 파이핑할 때 원시 패턴 공간을 일치하는 문자열로 제한합니다.

텍스트 파일이 있고 내부의 모든 공백을 하이픈으로 바꾸고 싶습니다 [[( ]]괄호는 중첩되지 않으며 항상 일치합니다). 아래는 예입니다:

$ cat test.txt 
abc [[foo]] xyz
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz [[something else]]

따라서 원하는 출력은 다음과 같습니다.

abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]

sed나는 대괄호 안의 문자열을 일치시킨 다음 대체할 플래그로 e결과를 다시 실행하는 데 사용할 수 있다고 생각했습니다 . sed그러나 문제는 일치하는 문자열이 명령으로 실행될 뿐만 아니라 전체 패턴 공간(전체 라인처럼 보입니다)도 실행된다는 것입니다.

$ sed -E 's@(\[\[)(.+)(\]\])@sed -e "s/ /-/g" <<< "\1\2\3"@gpe' test.txt 
abc sed -e "s/ /-/g" <<< "[[foo]]" xyz
sh: 1: Syntax error: redirection unexpected

abc sed -e "s/ /-/g" <<< "[[foo bar]]" xyz
sh: 1: Syntax error: redirection unexpected

abc sed -e "s/ /-/g" <<< "[[foo bar baz]]" xyz
sh: 1: Syntax error: redirection unexpected

e플래그를 통해 실행되는 항목을 일치하는 문자열로 제한하는 방법이 있습니까 ? 그렇지 않다면 이 문제를 어떻게 해결해야 합니까 sed?

답변1

쉘에 전달되는 수정자를 제한하는 방법은 없지만 e다음과 같이 할 수 있습니다.

$ sed -E ':a;s@(.*\[\[)([^][]* [^][]*)(\]\].*)@printf "%s%s%s" "\1" "$(printf "\2" | sed "s/ /-/g")" "\3"@e;ta' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]

다중 대체 처리는 루프에서 수행되며 일치의 욕심 많은 특성으로 인해 실제로 대체가 역순으로 수행됩니다.

또한 which를 e사용하면 입력 리디렉션을 /bin/sh지원하지 않을 수 있다는 점에 유의하세요 <<<(따라서 동등한 파이프를 사용하세요 printf "\2" | sed "s/ /-/g").


Perl이 옵션인 경우 다음과 같이 원래 의도에 더 가까운 작업을 수행할 수 있습니다.

$ perl -pe 's/(?<=\[\[)(.*?)(?=\]\])/$1 =~ s: :-:rg/ge' test.txt
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]

Perl은 non-greedy 수정자를 제공하므로 ?, g외부 대체에 대한 플래그를 사용하여 한 줄에 여러 대체를 보다 일반적으로 처리할 수 있습니다.

답변2

사용 표준 sed:

$ sed -e ':again' -e 's/\(\[\[[^]]*\) \([^]]*\]\]\)/\1-\2/g' -e 't again' file
abc [[foo]] xyz
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz [[something-else]]

[[]]그러면 와 사이에 있는 모든 공백 문자가 대시로 대체됩니다 . 이는 및 사이의 공백 문자(선택적으로 다른 문자열 옆에 위치)를 [[일치시켜 수행 됩니다. ]]일치하는 하위 문자열은 있는 그대로 대체되며 공백은 대시로 대체됩니다.

교체가 완료되면 이 명령을 사용하면 스크립트가 다른 교체를 위해 t레이블로 다시 분기됩니다 . again이는 일치 항목이 겹치기 때문에 처음에 놓친 공간을 설명할 수 있습니다.

왜냐하면 모든 사람은 항상 (아마도) [[관련이 있다고 하기 때문입니다.]]같은 줄에), 명령을 약간 단축할 수 있습니다.

sed -e ':again' -e 's/\(\[\[[^]]*\) /\1-/g' -e 't again' file

이것은 종결을 찾고 있지 않습니다 ]].

답변3

cat - <<\! > file
Abc [[ \ ]] def and a cup of 
Ghi [[]] jkl 
Mno [[ ]] pqr 
abc [[" \' \\\"]] xyz
abc [[foo$$]] xyz [[a b c]] deal
abc [[foo bar]] xyz
abc [[foo bar baz]] xyz
abc [[foo $bar baz]] xyz [[FOO BAR VAZ]] $#
!
  1. GNU sed/e수정자 에 의지할 필요가 없습니다.
sed -Ee '
  :loop
    s/([[]{2}[^][]*) ([^]]*]])/\1-\2/
  t loop
' file

Posixly 방식으로 작성하는 것은 간단하지만 확장 정규식 모드가 활성화된 상태에서 사용하는 백슬래시를 최소화하기 위해 -E루프는 "[[...]]" 쌍 공백 문자 발견/반복 사이를 점진적으로 전환합니다. 어떤 쌍에서도 그러한 공간을 찾지 못하면 루프가 중지됩니다. 그런 다음 패턴 공간을 인쇄하고 다음 줄을 패턴 공간으로 읽습니다... 린스... eof가 보일 때까지 반복합니다.


  1. | 유틸리티를 사용하여 awk문자열의 각 줄을 분할합니다 . 대칭성([[n]])이 쌍으로 이 순서대로 발생하기 때문에 이를 수행할 수 있습니다. 매달린 [[ 또는 ]]이 없습니다. 그런 다음 모든 짝수 필드는 [[ n ]] 내에 있으며 처리되어야 합니다.[[]]
awk -F '[[]{2}|]]' '
  {
    for (i=2; i<=NF; i+=2) {
      gsub(/ /, "-", $i)
      $i = "[[" $i "]]"
    }
  }1
' OFS= file

3. 수정자를 GNU sed사용하십시오 /e.

sed -Ee "
  s/'/&\"&\"&/g;tloop
  :loop
    s|(.*[[]{2})([^][]* [^]]*)(]].*)|v='\2';v=\${v// /-};printf '%s' '\1' \"\$v\" '\3'|e
  t loop
" file

  1. 위의 awk와 같은 방식으로 Perl을 사용합니다.
perl -F'(\[\[|]])' -lane 'my $i;
  print map { ++$i%4 == 3 ? tr/ /-/r : $_ } @F;
' file

  1. GNU sed는 [[...]] 쌍을 절단한 다음 다시 병합하여 격리된 쌍을 변환합니다. 모든 쌍이 검사될 때까지 이 작업을 계속하십시오.
m='[^\n]'
sed -Ee "
  s/[[]{2}|]]/&\n/g;T;h
  :loop
    s/^$m*\n($m*)\n.*/\1/
    y/ /-/;G
    s/^($m*)\n($m*)\n$m*\n/\2\1/
    h
  /\n/b loop
" file

산출:

Abc [[-\--]] def 
Ghi [[]] jkl 
Mno [[-]] pqr 
abc [["-\'-\\\"]] xyz
abc [[foo$$]] xyz [[a-b-c]] deal
abc [[foo-bar]] xyz
abc [[foo-bar-baz]] xyz
abc [[foo-$bar-baz]] xyz [[FOO-BAR-VAZ]] $#

관련 정보