Kann ich sed verwenden, um Zeichen wie mit tr zu übersetzen?

14

Ich möchte eine Reihe von Zeichen durch entsprechende Zeichen aus einer anderen Reihe ersetzen, etwa so:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

Übersetzungen / Transliterationen wie diese sind die Spezialität des trBefehls:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

Leider wird trdas Ändern von Dateien an Ort und Stelle nicht unterstützt, wie dies der sedFall ist.
Ich würde es gerne verwenden, seddamit ich das Rad des Jonglierens von temporären Dateien nicht neu erfinden muss.

n.st
quelle
Diese Frage hat sich selbst beantwortet, da ich anscheinend keine Ergebnisse für "sed translate characters" gefunden habe. Das magische Schlüsselwort endete mit "transliterieren", aber ich dachte, es lohnt sich, diese Funktion so leicht wie möglich auffindbar zu machen.
13.
Beachten Sie Folgendes, wenn Sie versuchen, Problemumgehungen für dieses Problem zu implementieren: tr(Richtig) Ignoriert die Rekursion in den Ersatzsätzen: echo 'abc' | tr ab bxbxc. Bei einer primitiven Lösung wird dies möglicherweise abgeschlachtet, xxcda die Übersetzung erneut auf bereits übersetzte Zeichen angewendet wird.
13.
Verwandt: tr analog für Unicode-Zeichen? (GNU im sedGegensatz zu GNU trkann Multi-Byte-Zeichen transkribieren)
Stéphane Chazelas
Wenn Sie eine andere Möglichkeit haben möchten: Perl kann übersetzen, und -i und (sofern nicht schon uralt) Multibyte. Nicht POSIX, aber ziemlich häufig.
Dave_thompson_085

Antworten:

24

sedhat den yBefehl, der wie folgt funktioniert tr:

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

Der yBefehl ist Teil der POSIX- sedSpezifikation und sollte daher auf nahezu jeder Plattform funktionieren.

Und da dies der sedFall ist, können Sie eine Datei durch die bearbeitete Version ersetzen , wodurch Sie das lästige Geschäft mit temporären Dateien vermeiden (vorausgesetzt, Ihre Implementierung sedunterstützt die -iOption, die von POSIX nicht angegeben wird):

$ sed -i 'y/ots/u.x/' some-file.txt
n.st
quelle
@ StéphaneChazelas Danke für den Hinweis; Ich war mir der inneren Funktionsweise bis jetzt nicht bewusst. Ich habe meine Antwort bearbeitet, um das zu erwähnen.
13.
Danke, das ist außerordentlich nützlich! Ich hatte erwartet, dass es in VIM (8.0.1092 unter CentOS 7.3) funktioniert, aber das ist nicht der Fall. Sollte nicht irgendetwas Sed tun, was VIM tut?
Dotancohen
1
@dotancohen Nur weil Vims Substitutionsfunktion nach der von Vim modelliert ist sed, heißt das nicht, dass auch die anderen Funktionen so sind. ;) Die Vim-Mailingliste enthält einen Thread zum Auffinden eines y/abc/def/Äquivalents. Die beste Option scheint zu sein :%call setline(".", tr(getline("."),"abc","def")).
14.
8

Wenn Sie wie in Ihrem Fall Zeichen transliterieren, ohne ihre Größe zu ändern (einige Implementierungen wie GNU unterstützen jedoch trnur Einzelbyte-Zeichen), können Sie Folgendes tun:

tr 'ots' 'u.x' < file 1<> file

Das heißt, habe trdie Datei über sich selbst überschrieben.

Das ist besser als sed -iaus mehreren Gründen:

  • Es wird kein zusätzlicher Speicherplatz benötigt (außer für einige Dateien mit geringer Dichte, Sonderfälle beim Kopieren beim Schreiben).
  • Es bewahrt Inode-Nummern, Eigentumsrechte, Berechtigungen, ACLs ...
  • Mit Symlinks funktioniert es einwandfrei, harte Links werden nicht unterbrochen
  • Es lässt keine temporären Dateien liegen, wenn sie getötet werden.

Ein Nachteil ist, dass die Datei bei einer Unterbrechung zur Hälfte übersetzt wird (in diesem Fall können Sie sie jedoch erneut ausführen, um sie zu beenden). Einige sedImplementierungen würden dies korrekt handhaben, indem sie sicherstellen, dass die ursprüngliche Datei unverändert bleibt, es sei denn, der Befehl ist erfolgreich.

Stéphane Chazelas
quelle
3
Führen Sie die Übersetzung vorsichtig erneut aus, wenn Sie eine Rekursion in den Übersetzungssätzen haben, z echo 'abc' | tr ab bx.
13.
1
@ n.st, ja, deshalb habe ich in diesem Fall gesagt , obwohl ich zustimme, dass es sich lohnt, es auszusprechen .
Stéphane Chazelas
Am Ende musste ich immerhin mit temporären Dateien arbeiten: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - Die Multibyte-Zeichen machten es unmöglich, GNU zu verwenden, trund in unserer symlinklastigen PXE-Umgebung gab sed -ies ein Problem zu passieren ...: /
n.st
@ n.st, iconv -t cp437scheint dafür besser geeignet zu sein.
Stéphane Chazelas
iconvbricht ab, wenn die Eingabedatei bereits cp437-codierte Bytes oder eine Mischung aus mehreren Codierungen enthält. Während es im Allgemeinen vorzuziehen ist, ist es in diesem Fall robuster, manuelle Ersetzungen vorzunehmen.
14.
4

Wenn das Hauptproblem in der mangelnden Unterstützung für das Ändern von Dateien besteht, ist das spongeTool aus dem moreutils-Paket möglicherweise für Sie von Interesse :

tr 'ots' 'u.x' < file | sponge file

schreibt in file, wird aber erst filezum Schreiben geöffnet , wenn die Eingabe abgeschlossen ist. Aus der Manpage :

spongeLiest die Standardeingabe und schreibt sie in die angegebene Datei. Im Gegensatz zu einer Shell-Umleitung nimmt der Schwamm alle Eingaben auf, bevor die Ausgabedatei geöffnet wird. Auf diese Weise können Pipelines erstellt werden, die aus derselben Datei lesen und in dieselbe Datei schreiben.

Es sei denn, Sie haben wirklich große Dateien, die nicht im Speicher gehalten werden können, spongekönnten für Sie arbeiten.

Mindriot
quelle
2
Ein Problem mit spongeist, dass es immer noch überschreibt, filewenn trfehlschlägt (zum Beispiel, wenn Sie schreiben, aber keinen Lesezugriff hatten file)
Stéphane Chazelas
Oh ja, das tut es. Das habe ich nicht erwartet. Vielen Dank.
Mindriot
Siehe den cat file >; fileOperator von ksh93, der die Ausgabe in ein tempfile schreibt, das nur dann in das Ziel umbenannt wird, wenn der Befehl erfolgreich ausgeführt wurde (aber so sed -iwird eine neue Datei erstellt, anstatt das Original zu überschreiben).
Stéphane Chazelas