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
awk
perl
여기서와 동일한 작업을 효과적으로 수행할 수 있습니다.더 간단하다, 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]
여기서 는subexpr
배열의 요소를 읽거나 설정하는 문자열 값 표현식입니다.$number
예를 들어$1 $2
필드를 참조하고$0
전체 기록을 참조합니다. 위의 내용에 따르면 이 작업은key.txt
파일의 마지막 줄인$1
is3
및$2
is 와 같은 의 줄에서만 수행되며 index 및 contentsource-three
가 포함된 배열 항목을 저장합니다 . 이 값을 선택한 이유는 아래를 참조하세요. and 는 이스케이프 를 사용하는 문자열 리터럴이고 실제 값은 입니다. 반면 while 은 바로 그 것이며 사이에 연산자가 없는 문자열 피연산자가 연결됩니다. 이 작업을 마지막으로 수행한다는 것은 이 레코드에 대한 나머지 스크립트를 건너뛰고 루프의 맨 위로 돌아가서 다음 레코드를 시작하는 것을 의미합니다.\[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].