Ich suche nach einer Möglichkeit, Platzhalterzeichenfolgen in einer Vorlagendatei durch konkrete Werte durch gängige Unix-Tools (bash, sed, awk, möglicherweise perl) zu ersetzen. Es ist wichtig, dass der Austausch in einem einzigen Durchgang erfolgt, dh was bereits gescannt / ersetzt wurde, darf bei einem anderen Austausch nicht berücksichtigt werden. Diese beiden Versuche schlagen beispielsweise fehl:
echo "AB" | awk '{gsub("A","B");gsub("B","A");print}'
>> AA
echo "AB" | sed 's/A/B/g;s/B/A/g'
>> AA
Das richtige Ergebnis ist in diesem Fall natürlich BA.
Im Allgemeinen sollte die Lösung dem Scannen der Eingabe von links nach rechts entsprechen, um eine längste Übereinstimmung mit einer der angegebenen Ersatzzeichenfolgen zu erzielen, und für jede Übereinstimmung eine Ersetzung durchzuführen und von diesem Punkt an in der Eingabe fortzufahren (keine der Bereits gelesene Eingaben oder die durchgeführten Ersetzungen sollten für Übereinstimmungen berücksichtigt werden. Tatsächlich spielen die Details keine Rolle, nur dass die Ergebnisse des Austauschs weder ganz noch teilweise für einen anderen Ersatz berücksichtigt werden.
HINWEIS Ich suche nur nach korrekten generischen Lösungen. Bitte schlagen Sie keine Lösungen vor, die für bestimmte Eingaben (Eingabedateien, Suchen und Ersetzen von Paaren) fehlschlagen, auch wenn dies unwahrscheinlich erscheint.
tr AB BA
.Antworten:
OK, eine allgemeine Lösung. Die folgende Bash-Funktion erfordert
2k
Argumente. Jedes Paar besteht aus einem Platzhalter und einem Ersatz. Es liegt an Ihnen, die Zeichenfolgen entsprechend zu zitieren, um sie an die Funktion zu übergeben. Wenn die Anzahl der Argumente ungerade ist, wird ein implizites leeres Argument hinzugefügt, das das Auftreten des letzten Platzhalters effektiv löscht.Weder Platzhalter noch Ersetzungen dürfen NUL-Zeichen enthalten, aber Sie können Standard-C-
\
Landschaften verwenden, z. B.\0
wenn SieNUL
s benötigen (und folglich müssen Sie schreiben,\\
wenn Sie a möchten\
).Es erfordert die Standard-Build-Tools, die auf einem posix-ähnlichen System (lex und cc) vorhanden sein sollten.
Wir gehen davon aus, dass dies
\
bei Bedarf bereits in den Argumenten maskiert ist, aber wir müssen doppelte Anführungszeichen vermeiden, falls vorhanden. Das ist es, was das zweite Argument zum zweiten printf bewirkt. Da dielex
Standardaktion istECHO
, müssen wir uns darüber keine Sorgen machen.Beispiellauf (mit Timings für Skeptiker; es ist nur ein billiger Standard-Laptop):
Für größere Eingaben kann es nützlich sein, ein Optimierungsflag für bereitzustellen
cc
, und für die aktuelle Posix-Kompatibilität ist es besser, diese zu verwendenc99
. Eine noch ehrgeizigere Implementierung könnte versuchen, die generierten ausführbaren Dateien zwischenzuspeichern, anstatt sie jedes Mal zu generieren, aber ihre Generierung ist nicht gerade teuer.Bearbeiten
Wenn Sie über tcc verfügen , können Sie den Aufwand beim Erstellen eines temporären Verzeichnisses vermeiden und die schnellere Kompilierungszeit genießen, die bei Eingaben normaler Größe hilfreich ist:
quelle
fn() { tcc ; } <<CODE\n$(gen code)\nCODE\n
. Kann ich fragen - das ist eine großartige Antwort und ich habe sie sofort nach dem Lesen positiv bewertet -, aber ich verstehe nicht, was mit dem Shell-Array passiert? Was macht"${@//\"/\\\"}"
das?So etwas ersetzt jedes Vorkommen Ihrer Zielzeichenfolgen immer nur einmal, da sie
sed
in Streams mit einem Biss pro Zeile auftreten. Dies ist der schnellste Weg, den ich mir vorstellen kann. Dann wieder, ich schreibe C. nicht , aber diese nicht zuverlässig null delimiters handhaben, wenn Sie es wünschen. In dieser Antwort erfahren Sie, wie es funktioniert. Dies hat keine Probleme mit enthaltenen speziellen Shell-Zeichen oder ähnlichem - aber es ist ASCII-länderspezifisch oder gibt mit anderen Wortenod
keine Mehrbyte-Zeichen in derselben Zeile aus und führt nur eines pro Zeichen aus. Wenn dies ein Problem ist, möchten Sie hinzufügeniconv
.quelle
sed
und bis zu einer Null oder so etwas speichern, um dannsed
das Skript dieses einen schreiben zu lassen. oder setzen Sie es in eine Shell-Funktion und geben Sie ihm Werte bei einem Biss pro Zeile wie"/$1/"
..."/$2/"
- vielleicht schreibe ich diese Funktionen auch ...PLACE1
,PLACE2
undPLA
.PLA
gewinnt immer. OP sagt: "Entspricht dem Scannen der Eingabe von links nach rechts für eine längste Übereinstimmung mit einer der angegebenen Ersatzzeichenfolgen" (Hervorhebung hinzugefügt)Eine
perl
Lösung. Selbst wenn einige angaben, dass dies nicht möglich ist, habe ich eines gefunden, aber im Allgemeinen ist ein einfaches Abgleichen und Ersetzen nicht möglich, und selbst wenn es aufgrund des Zurückverfolgens einer NFA schlechter wird, kann das Ergebnis unerwartet sein.Im Allgemeinen, und dies muss gesagt werden, führt das Problem zu unterschiedlichen Ergebnissen, die von der Reihenfolge und Länge der Ersatztupel abhängen. dh:
und die Eingabe
AAA
ergibtBBB
oderCCB
.Hier der Code:
Checkerbunny:
quelle