목록을 기반으로 sed 대체(s///g)를 수행하는 방법은 무엇입니까? 여러 단어를 해당하는 다른 단어로 바꿔야 합니다.

목록을 기반으로 sed 대체(s///g)를 수행하는 방법은 무엇입니까? 여러 단어를 해당하는 다른 단어로 바꿔야 합니다.

sed나는 이 질문이 이전에 요청된 적이 없다고 생각하므로 이것이 가능한지 모르겠습니다 .

문장에 단어로 확장해야 하는 숫자가 많이 있다고 가정합니다. 실제적인 예는 일반적인 에세이에서 번호가 매겨진 인용을 MLA 형식으로 바꾸는 것입니다.

essay.txt:

Sentence 1 [1]. sentence two [1][2]. Sentence three[1][3].

Key.txt(이것은 탭으로 구분된 파일입니다):

1   source-one
2   source-two
3   source-three
...etc

예상되는 Result.txt:

Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three]

다음은 의사 코드 시도이지만 제대로 이해하지 못 sed하거나 tr제대로 수행하지 못했습니다.

 cat essay.txt | sed s/$(awk {print $1} key.txt)/$(awk {print $2} key.txt)/g

추신: 여러 용어를 사용하여 대량 찾기 및 바꾸기를 위한 메모장++의 트릭이 있다면 좋을 것입니다. 현재로서는 찾기 및 바꾸기가 한 번에 한 용어에 대해서만 작동하는 것처럼 보이지만 한 번에 여러 용어에 대해 일괄적으로 수행할 수 있는 방법이 필요합니다.

답변1

대신 다음을 사용해야 합니다 perl.

$ perl -ne '
  ++$nr;
  if ($nr == $.) {
    @w = split;
    $k{$w[0]} = $w[1];
  }
  else {
    for $i (keys %k) {
      s/(\[)$i(\])/$1.$k{$i}.$2/ge
    }
    print;
  }
  close ARGV if eof;
' key.txt essay.txt
Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three]

답변2

awkperl여기 와 똑같이 효과적으로 할 수 있습니다조금 더 간단하다, GNU 이외의 구현에서는 (큰?) 텍스트 파일을 불필요하게 분할하여 약간의 CPU 시간을 낭비할 수 있습니다.

awk 'NR==FNR{a["\\["$1"\\]"]="["$2"]";next} {for(k in a) gsub(k,a[k]);print}' key.txt essay.txt

당신이 요청했기 때문에설명:

  • awk패턴-액션 쌍으로 구성된 '스크립트'를 사용하여 작동한 다음 하나 이상의 파일(또는 표준 입력)을 한 번에 하나의 '레코드'로 읽습니다. 기본적으로 각 레코드는 한 줄이고 각 레코드에 대해 다음과 같이 필드로 나눕니다. 기본적으로 공백(탭 포함)을 사용하고 (별도의 지시가 없는 한) 각 패턴을 테스트하고(종종 현재 레코드 및/또는 해당 필드를 확인함) 작업 실행과 일치하는지(종종 다음 작업을 수행함) 스크립트를 적용합니다. 또는 해당 레코드 및/또는 필드와 함께). 여기서는 두 파일을 지정하여 key.txt essay.txt해당 두 파일을 한 줄씩 순서대로 읽습니다. 스크립트~할 수 있다명령줄 대신 파일에 넣을 수 있지만 여기서는 그렇게 하지 않기로 결정했습니다.

  • 첫 번째 패턴은 입니다 NR==FNR. NR처리 중인 레코드의 번호를 나타내는 내장 변수입니다. FNR마찬가지로 현재 입력 파일 내의 레코드 번호입니다. 첫 번째 파일( key.txt)의 경우 이는 동일합니다. 두 번째 파일(및 기타 파일)의 경우 동일하지 않습니다.

  • 첫 번째 작업은 입니다 {a["\\["$1"\\]"]="["$2"]";next}. awk'연관' 또는 '해시' 배열이 있습니다. 문자열 값 표현식은 배열의 요소를 읽거나 설정합니다 arrayname[subexpr]. 예를 들어 등은 필드를 참조하고 전체 레코드를 참조합니다. 위의 내용에 따라 이 작업은 의 행에 대해서만 실행됩니다. 예를 들어 해당 파일의 마지막 행은 과 is 이며 , 이는 아래 첨자 및 내용이 있는 배열 항목을 저장합니다 . 내가 이 값을 선택한 이유는 아래를 참조하세요. and 는 실제 값이 와 while 인 이스케이프를 사용하는 문자열 리터럴이며 , 사이에 연산자가 없는 문자열 피연산자가 연결됩니다. 마지막으로 이 작업이 실행됩니다. 즉, 이 레코드에 대한 나머지 스크립트를 건너뛰고 루프의 맨 위로 돌아가서 다음 레코드에서 시작하면 됩니다.subexpr$number$1 $2$0key.txt$13$2source-three\[3\][source-three]"\\[""\\]"\[\]"[" "]"[ ]next

  • 두 번째 패턴은 비어 있으므로 두 번째 파일의 모든 줄과 일치하고 작업을 실행합니다 {for(k in a) gsub(k,a[k]);print}. 이 for(k in a)구성은 Bourne 유형 쉘이 에서 수행하는 것과 매우 유사한 루프를 생성합니다 for i in this that other; do something with $i; done. 단, 여기에서 의 값은 k다음 과 같습니다.아래첨자배열의 a. 각 값에 대해 gsub주어진 정규식과 일치하는 모든 항목을 찾아 주어진 문자열로 바꾸는 (전역 대체)를 실행합니다. 예를 들어 \[3\]텍스트 문자열과 일치하는 정규식 [3][source-three]이러한 모든 일치 항목을 대체하려는 텍스트 문자열이 되도록 배열(위)에서 아래 첨자와 내용을 선택했습니다. 기본적으로 gsub현재 레코드에서 작동합니다 . $0모든 값에 대해 이 대체를 수행한 후 기본적으로 현재 상태 그대로 출력하고 원하는 모든 대체가 수행된 상태로 a실행됩니다 .print$0

참고: 특히 Linux에서 일반적이지만 보편적이지는 않은 GNU awk(gawk)에는 실행된 패턴이나 작업에 필드 값이 필요한 항목이 없는 경우 실제로 필드 분할을 수행하지 않는 최적화 기능이 있습니다. 다른 구현에서는 약간의 CPU 시간이 낭비될 수 있으며, cuonglm의 perl방법은 이를 방지하지만 파일이 엄청나지 않으면 눈에 띄지도 않을 것입니다.

답변3

bash$ sed -f  <( sed -rn 's#([0-9]+)\s+(.*)#s/\\[\1]/[\2]/g#p' key.txt ) essay.txt

Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three].

답변4

이를 달성하기 위해 루프 내에서 내부 sed 대체를 사용할 수 있습니다.

$ cp essay.txt Result.txt
$ while read n k; do sed -i "s/\[$n\]/\[$k\]/g" Result.txt; done < key.txt
$ cat Result.txt 
Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three].

관련 정보