%20basierend%20auf%20einer%20Liste%20durch%3F%20Ich%20muss%20mehrere%20W%C3%B6rter%20durch%20andere%20entsprechende%20W%C3%B6rter%20ersetzen.png)
Ich glaube nicht, dass diese Frage schon einmal gestellt wurde, daher weiß ich nicht, ob sed
dies möglich ist.
Angenommen, ich habe in einem Satz eine Reihe von Zahlen, die ich in Wörter ausbauen muss. Ein praktisches Beispiel hierfür wäre das Austauschen der nummerierten Zitate in einem typischen Essay in das MLA-Format:
essay.txt
:
Sentence 1 [1]. sentence two [1][2]. Sentence three[1][3].
Key.txt
(dies ist eine durch Tabulatoren getrennte Datei):
1 source-one
2 source-two
3 source-three
...etc
Erwartet Result.txt
:
Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three]
Hier ist mein Pseudocode-Versuch, aber ich verstehe nicht genug davon sed
oder tr
um es richtig zu machen:
cat essay.txt | sed s/$(awk {print $1} key.txt)/$(awk {print $2} key.txt)/g
PS: Wenn es in Notepad++ einen Trick für Massensuchen und -ersetzen mit mehreren Begriffen gäbe, wäre das großartig. Derzeit scheint Suchen und Ersetzen nur für einen Begriff auf einmal zu funktionieren, aber ich brauche eine Möglichkeit, es en masse für viele Begriffe auf einmal zu tun.
Antwort1
Sie sollten perl
stattdessen verwenden:
$ 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]
Antwort2
awk
kann effektiv das gleiche tun wie perl
hieretwas einfacher, obwohl andere Implementierungen als GNU möglicherweise ein wenig CPU-Zeit durch unnötiges Aufteilen der (großen?) Textdatei verschwenden:
awk 'NR==FNR{a["\\["$1"\\]"]="["$2"]";next} {for(k in a) gsub(k,a[k]);print}' key.txt essay.txt
Da Sie gefragt habenErläuterung:
awk
funktioniert, indem ein „Skript“ aus Muster-Aktions-Paaren genommen wird, dann eine oder mehrere Dateien (oder die Standardeingabe) einen „Datensatz“ nach dem anderen liest, wobei jeder Datensatz standardmäßig eine Zeile ist, und jeden Datensatz standardmäßig an Leerzeichen (einschließlich Tabulatoren) in Felder aufteilt und das Skript anwendet, indem es nacheinander (sofern nicht anders angegeben) jedes Muster testet (wobei häufig der aktuelle Datensatz und/oder seine Felder betrachtet werden) und, wenn es übereinstimmt, die Aktion ausführt (was häufig etwas mit dem besagten Datensatz und/oder den Feldern macht). Hier gebe ich zwei Dateien an,key.txt essay.txt
also liest es diese beiden Dateien in dieser Reihenfolge, Zeile für Zeile. Das Skriptdürfenin eine Datei statt in die Befehlszeile eingegeben werden, aber hier habe ich mich dagegen entschieden.das erste Muster ist
NR==FNR
.NR
ist eine eingebaute Variable, die die Nummer des zu verarbeitenden Datensatzes ist;FNR
ist ebenso die Nummer des Datensatzes in der aktuellen Eingabedatei. Für die erste Datei (key.txt
) sind diese gleich; für die zweite Datei (und alle anderen) sind sie ungleichdie erste Aktion ist
{a["\\["$1"\\]"]="["$2"]";next}
.awk
hat „assoziative“ oder „gehashte“ Arrays;arrayname[subexpr]
wobeisubexpr
ein String-Ausdruck ist, der ein Element des Arrays liest oder setzt.$number
zB$1 $2
usw. verweisen auf die Felder und$0
verweisen auf den gesamten Datensatz. Wie oben wird diese Aktion nur für Zeilen in ausgeführt, also istkey.txt
beispielsweise in der letzten Zeile dieser Datei und ist , und dies speichert einen Array-Eintrag mit einem Index von und einem Inhalt von ; siehe unten, warum ich diese Werte gewählt habe. Und sind String-Literale mit Escape-Zeichen, deren tatsächliche Werte und sind, während nur sind , und String-Operanden ohne Operator dazwischen werden verkettet. Schließlich wird diese Aktion ausgeführt, was bedeutet, dass der Rest des Skripts für diesen Datensatz übersprungen wird. Gehen Sie einfach zurück zum Anfang der Schleife und beginnen Sie mit dem nächsten Datensatz.$1
3
$2
source-three
\[3\]
[source-three]
"\\["
"\\]"
\[
\]
"[" "]"
[ ]
next
das zweite Muster ist leer, also passt es zu jeder Zeile in der zweiten Datei und führt die Aktion aus
{for(k in a) gsub(k,a[k]);print}
. Diefor(k in a)
Konstruktion erzeugt eine Schleife, ähnlich wie Bourne-artige Shells infor i in this that other; do something with $i; done
, außer dass hier die Werte vonk
dieIndizesdes Arraysa
. Für jeden solchen Wert wird (globaler Ersatz) ausgeführt,gsub
der alle Übereinstimmungen eines gegebenen regulären Ausdrucks findet und sie durch eine gegebene Zeichenfolge ersetzt; ich habe die Indizes und Inhalte im Array (oben) so gewählt, dass beispielsweise\[3\]
ein regulärer Ausdruck ist, der mit der Textzeichenfolge übereinstimmt,[3]
und[source-three]
die Textzeichenfolge ist, die Sie für jede solche Übereinstimmung ersetzen möchten.gsub
arbeitet standardmäßig mit dem aktuellen Datensatz$0
. Nachdem diese Ersetzung für alle darin enthaltenen Wertea
durchgeführt wurde, wird ausgeführtprint
, was standardmäßig die aktuelle$0
Ausgabe mit allen gewünschten Ersetzungen ergibt.
Hinweis: GNU awk (gawk), das vor allem unter Linux weit verbreitet, aber nicht universell ist, verfügt über eine Optimierung, bei der die Feldaufteilung nicht durchgeführt wird, wenn in den ausgeführten Mustern oder Aktionen nichts die Feldwerte benötigt. Bei anderen Implementierungen kann ein wenig CPU-Zeit verschwendet werden, was die perl
Methode von cuonglm vermeidet, aber wenn Ihre Dateien nicht riesig sind, wird dies wahrscheinlich nicht einmal auffallen.
Antwort3
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].
Antwort4
Sie können die direkte sed-Ersetzung innerhalb einer Schleife verwenden, um dies zu erreichen:
$ 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].