Wie führe ich eine sed-Substitution (s///g) basierend auf einer Liste durch? Ich muss mehrere Wörter durch andere entsprechende Wörter ersetzen

Wie führe ich eine sed-Substitution (s///g) basierend auf einer Liste durch? Ich muss mehrere Wörter durch andere entsprechende Wörter ersetzen

Ich glaube nicht, dass diese Frage schon einmal gestellt wurde, daher weiß ich nicht, ob seddies 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 sedoder trum 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 perlstattdessen 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

awkkann effektiv das gleiche tun wie perlhieretwas 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:

  • awkfunktioniert, 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.txtalso 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. NRist eine eingebaute Variable, die die Nummer des zu verarbeitenden Datensatzes ist; FNRist 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 ungleich

  • die erste Aktion ist {a["\\["$1"\\]"]="["$2"]";next}. awkhat „assoziative“ oder „gehashte“ Arrays; arrayname[subexpr]wobei subexprein String-Ausdruck ist, der ein Element des Arrays liest oder setzt. $numberzB $1 $2usw. verweisen auf die Felder und $0verweisen auf den gesamten Datensatz. Wie oben wird diese Aktion nur für Zeilen in ausgeführt, also ist key.txtbeispielsweise 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.$13$2source-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}. Die for(k in a)Konstruktion erzeugt eine Schleife, ähnlich wie Bourne-artige Shells in for i in this that other; do something with $i; done, außer dass hier die Werte von kdieIndizesdes Arrays a. Für jeden solchen Wert wird (globaler Ersatz) ausgeführt, gsubder 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. gsubarbeitet standardmäßig mit dem aktuellen Datensatz $0. Nachdem diese Ersetzung für alle darin enthaltenen Werte adurchgeführt wurde, wird ausgeführt print, was standardmäßig die aktuelle $0Ausgabe 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 perlMethode 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].

verwandte Informationen