Posso usare `sed` per tradurre caratteri come con` tr`?


14

Vorrei sostituire un set di caratteri con caratteri corrispondenti di un altro set, qualcosa del genere:

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

foobartest → fuubar.ex.

Traduzioni / traslitterazioni come questa sono la specialità del trcomando:

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

Sfortunatamente trnon supporta la modifica dei file sul posto come sedfa.
Vorrei utilizzarlo in sedmodo da non dover reinventare la ruota dei file temporanei di giocoleria.


Rispondi a questa domanda da solo poiché non riesco a trovare alcun risultato per "sed translate caratteri". La parola chiave magica ha finito per essere "traslitterata", ma ho pensato che valesse la pena rendere questa funzione il più facilmente possibile trovare.
n.

Qualcosa da tenere a mente quando si cerca di implementare soluzioni alternative per questo: tr(correttamente) ignora la ricorsione nei set di sostituzione: echo 'abc' | tr ab bxbxc. Una soluzione primitiva potrebbe macellarla xxcperché riapplica la traduzione ai caratteri che sono già stati tradotti.
n.

Correlati: tr analogico per i caratteri Unicode? (GNU sedcontrariamente a GNU trpuò traslitterare caratteri multibyte)
Stéphane Chazelas,

Se vuoi un'altra possibilità: perl può tradurre, e -i e (se non antico) multibyte. Non POSIX, ma abbastanza comune.
dave_thompson_085

Risposte:


24

sedha il ycomando che funziona proprio come tr:

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

Il ycomando fa parte delle specifiche POSIXsed , quindi dovrebbe funzionare praticamente su qualsiasi piattaforma.

E poiché lo è sed, puoi farlo sostituire un file con la sua versione modificata, risparmiando il fastidioso business dei file temporanei (a condizione che l'implementazione di sedsupporti l' -iopzione, che non è specificata da POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt

@ StéphaneChazelas Grazie per averlo sottolineato; Fino ad ora non ero a conoscenza dei meccanismi interni. Ho modificato la mia risposta per menzionarlo.
n.

Grazie, questo è straordinariamente utile! Mi aspettavo che funzionasse in VIM (8.0.1092 su CentOS 7.3) ma non funziona. Non dovrebbe fare niente sed, VIM fare?
dotancohen,

1
@dotancohen Solo perché la funzione di sostituzione di Vim è modellata su quella di sednon significa che lo siano anche le altre funzioni. ;) La mailing list di Vim ha una discussione sulla ricerca di un y/abc/def/equivalente; l'opzione migliore sembra essere :%call setline(".", tr(getline("."),"abc","def")).
n.

8

Se come nel tuo caso, stai traslitterando caratteri senza modificarne le dimensioni (comunque, alcune implementazioni come GNU trsupportano solo caratteri a byte singolo), puoi fare:

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

Cioè, hanno trsovrascrivere il file su se stesso.

È meglio che sed -isu diversi account:

  • non necessita di spazio su disco aggiuntivo (ad eccezione di alcuni file sparsi, casi speciali di copia su scrittura)
  • conserva numeri di inode, proprietà, permessi, ACL ...
  • funziona bene con i collegamenti simbolici, non interrompe i collegamenti reali
  • non lascia in sospeso i file temporanei quando vengono uccisi.

Uno svantaggio è che se viene interrotto, il file finirà per essere tradotto per metà (in questo caso, tuttavia, è possibile eseguirlo nuovamente per finirlo). Alcune sedimplementazioni lo gestiranno correttamente assicurandosi che il file originale rimanga invariato a meno che il comando non abbia esito positivo.


3
Fai attenzione a rieseguire la traduzione se hai ricorsioni nei set di traduzioni, ad es echo 'abc' | tr ab bx.
n.

1
@ n.st, sì, è per questo che ho detto in questo caso , anche se sono d'accordo che vale la pena spiegarlo.
Stéphane Chazelas,

Alla fine, ho dovuto lavorare con i file temporanei dopo tutto: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - I caratteri multibyte hanno reso impossibile l'uso di GNU tre nel nostro ambiente PXE pesantemente collegato al link, sed -iera un'attesa sbagliata per succedere ...: /
n.

@ n.st, iconv -t cp437sembra più appropriato per questo.
Stéphane Chazelas,

iconvsi interrompe quando il file di input contiene già byte con codifica cp437 o una combinazione di più codifiche. Quindi, mentre è preferibile nel caso generale, è più robusto fare sostituzioni manuali su questo caso.
n.

4

In alternativa, se il problema principale è la mancanza di supporto per la modifica dei file sul posto, potresti essere interessato allo spongestrumento dal pacchetto moreutils :

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

scriverà su file, ma si aprirà fileper la scrittura solo quando l'input è completo. Dalla manpage :

spongelegge l'input standard e lo scrive nel file specificato. A differenza di un reindirizzamento della shell, la spugna assorbe tutto il suo input prima di aprire il file di output. Ciò consente di costruire pipeline che leggono e scrivono nello stesso file.

A meno che tu non abbia file di grandi dimensioni che non possono essere conservati in memoria, spongepotrebbe funzionare per te.


2
Un problema spongeè che sovrascrive comunque filese trfallisce (ad esempio se si aveva accesso in scrittura ma non in lettura file)
Stéphane Chazelas

Oh, davvero lo fa; Non me l'aspettavo. Grazie.
Mindriot

Vedi l' cat file >; fileoperatore di ksh93 che scrive l'output in un file temporaneo che viene rinominato nella destinazione solo se il comando ha esito positivo (ma simile sed -i, che crea un nuovo file invece di sovrascrivere l'originale).
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.