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
PS:メモ帳++に複数の用語を使用して一括検索と置換のためのトリックがある場合は良いでしょう。実際には、検索と置換は一度に1つの用語に対してのみ機能するように見えますが、同時に複数の用語に対してまとめて動作する方法が必要です。
答え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
パターンとアクションのペアで構成された「スクリプト」を取り、一度に1つの「レコード」で1つ以上のファイル(または標準入力)を読み取ると、各レコードはデフォルトで1行になり、各レコードのフィールドに分割されます。デフォルトでは、スペース(タブを含む)を使用し、各パターンを順番にテストして(別途指示がない限り)(通常は現在のレコードおよび/または対応するフィールドを確認)、一致するか(通常はスクリプトを適用するためのタスクを実行します)を行います。記載されている記録および/またはフィールドと共に)。ここでは2つのファイルを指定したので、key.txt essay.txt
2つのファイルをその順序で1行ずつ読みます。スクリプトできるコマンドラインではなくファイルにありますが、ここではそうしないことにしました。最初のパターンは、処理中のレコード番号を表す組み込み変数です。
NR==FNR
は現在の入力ファイルのレコード番号です。最初のファイル()では同じですが、2番目のファイル(および他のファイル)では同じではありません。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
2番目のパターンは空であるため、2番目のファイルのすべての行と一致し、操作を実行します
{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].