Sed - 두 문자열을 바꾸면서 그 사이의 내용을 유지하는 방법은 무엇입니까?

Sed - 두 문자열을 바꾸면서 그 사이의 내용을 유지하는 방법은 무엇입니까?

일부 DokuWiki 페이지를 MediaWiki 형식으로 변환하는 쉘 스크립트를 작성 중입니다.그 반대로. 각주에 문제가 있습니다.

DokuWiki에는 DokuWiki의 기본 각주 마크업에 확장 기능을 추가하는 플러그인이 있습니다. 그 중 하나는 메모에 이름을 추가하고 나중에 다시 사용할 수 있는 기능입니다. 예를 들어:

도쿠위키 미디어 위키
[(FOO>This is a footnote.)] <ref name="FOO">This is a footnote.</ref>
[(BAR>Another note in the same paragraph.)] <ref name="BAR">Another note in the same paragraph.</ref>

그리고sed쉽게 찾아 교체할 수 있습니다. "주석 이름"이 있는 각주와 관련된 내 스크립트 명령은 다음과 같습니다.

sed -ri 's@\[\(.*>@<ref name=\"XXX\">@g' dokuwiki-page.txt
sed -ri 's@\)\]@<\/ref>@g' dokuwiki-page.txt

그러나 물론 이름은 유지되지 않고 해당 이름이 있는 모든 주석에 새로운 일반 주석 이름 "XXX"를 적용할 뿐입니다. 따라서 위의 예에서 결과는 다음과 같습니다.

도쿠위키 미디어 위키
[(FOO>This is a footnote.)] <ref name="XXX">This is a footnote.</ref>
[(BAR>Another note in the same paragraph.)] <ref name="XXX">Another note in the same paragraph.</ref>

주석 이름(예제에서는 FOO 및 BAR)을 유지하는 데 도움이 필요합니다.나는 단지 다른 해결책을 받아들이는 것이 아닙니다.sed.

중요 사항:

  1. 각주 문장은 단락 중간에 나타날 수 있으며, 각주 이름이 있는 여러 참조는 같은 단락에 다른 이름으로 나타날 수 있습니다. (일명 Unix의 "매우 긴 줄" 단락)
  2. MediaWiki 마크업이 너무 많은 html 태그( sums 로 가득 차 있음 ) 를 사용하기 때문에 명령을 분할하여 [(첫 번째 명령에서 바꾸고 두 번째 명령에서 바꿀 수 없습니다 . 태그가 잘못 교체되었을 수 있습니다.><>
  3. 내부 [(...)]가 없는 것도 있습니다 . 대신에 >in처럼요 .[(This is a nameless note.)][(My_Note_Name>This is a named note.)]

답변1

perl탐욕스럽지 않은 반복 연산자와 함께 정규식을 사용하면 이런 종류의 작업이 훨씬 쉽습니다.

perl -i -pe 's{\[\((.*?)>(.*?)\)\]}{<ref name="$1">$2</ref>}g' your-file

-i및 는 -r비표준 sed옵션입니다. -i실제로 perl는 서로 호환되지 않는 방식이지만 일부 구현에 의해 복사됩니다.

perl여러 sed구현과 달리 줄 크기에도 제한이 없고 NUL 문자를 처리할 수 있으며 기본적으로 입력을 바이트 단위로 처리하므로 사용자의 로케일에서 텍스트로 디코딩할 수 없는 입력의 문제가 없습니다.

입력 중 일부에 가 [(...)]포함되어 있지 않을 가능성이 있는 경우 >정규식을 조정해야 합니다. 참조 레이블에 word 문자(ASCII 숫자 및 밑줄)만 포함된 경우 다음과 같을 수 있습니다.

perl -i -pe 's{\[\((\w+)>(.*?)\)\]}{<ref name="$1">$2</ref>}g' your-file

또 다른 방법은 모든 항목을 찾아 [(...)]별도의 단계로 바꾸는 것입니다.

perl -i -pe '
  s{\[\(.*?\)\]}{
    $& =~ s{\[\((.*?)>(.*)\)\]}{<ref name="$1">$2</ref>}r
  }ge' your-file

또한 다음을 사용하여 이름 없는 주석을 변경할 수도 있습니다 <ref>nameless</ref>.

perl -i -pe '
  s{\[\(.*?\)\]}{
    $& =~ s{\[\((?:(.*?)>)?(.*)\)\]}{
      "<ref" . (defined($1) ? qq( name="$1") : "") . ">$2</ref>"
    }re
  }ge' your-file

[(...)]또는 포함되지 않은 항목과 일치하는지 확인하려면 부정 예측 연산자를 사용하세요 )].

perl -i -pe 's{\[\(((?:(?!\)\]).)*?)>((?1))\)\]}{<ref name="$1">$2</ref>}g' your-file

답변2

최종 SED 방법:

나는 다음을 사용하여 해결책을 찾았습니다.sed및 정규식 그룹.

sed -Ei 's@\[\(([[:alnum:]_-]*)>([[:alnum:][:space:].!?:;,@#%$&<>-_]*)\)\]@<ref name=\"\1\">\2<\/ref>@g' dokuwiki-page.txt

설명하다:

  1. [(+ letters, numbers, underscores and dashes in any quantity+ >+ letters, numbers, spaces and punctuation+ 로 줄 찾기)]
    • 그룹 1: 문자, 숫자, 밑줄, 대시를 원하는 수만큼 사용할 수 있습니다.
    • 그룹 2: 문자, 숫자, 공백 및 대부분의 구두점을 사용할 수 있습니다. 어떤 이유로 [:punct:]이것은 잘 작동하지 않으며 큰 목록을 사용해야 합니다: .!?:;,@#%$&<>-_.
    • 여기서의 비결은 \1그룹을 사용하거나 참조 할 수 있다는 것입니다 \2. 변수에 저장하는 것과 같습니다.
    • 다른 항목이 포함되어 있기 때문에 .*대신 사용할 수 없습니다 . 따라서 동일한 단락에 다른 이름의 각주(일명 매우 긴 줄)가 있는 경우 정규식에는 첫 번째 각주부터 두 번째 각주 내용 끝까지 모든 내용이 포함됩니다. 정말 엉망이야!([[:alnum:]_-]*)>
  2. 이들 모두를 <ref name="+ group \1+ ">+ group \2+ 로 바꾸십시오 </ref>.
    • 여기서는 주변 콘텐츠를 교체하는 동안 유지하려는 콘텐츠를 역참조 \1하고 사용합니다 .\2

매우 매우 어렵습니다! 이 작업을 수행하는 방법을 알아내는 데 3일이 걸렸습니다. 그리고 너무 길어요. Perl을 선택하는 것이 좋습니다. 하지만 sed를 사용하는 더 쉬운 방법을 알고 계시다면 저에게 가르쳐 주세요. 저는 배우는 것을 좋아합니다!

읽기 제안:

  • 도허티, D., & 로빈스, A. (1997). SED 및 AWK. (두번째 버전). 오라일리.

관련 정보