¿Puedo usar `sed` para traducir caracteres como con` tr`?

14

Me gustaría reemplazar un conjunto de caracteres con los caracteres correspondientes de otro conjunto, algo como esto:

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

foobartest → fuubar.ex.

Traducciones / transliteraciones como esta son la especialidad del trcomando:

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

Lamentablemente trno admite el cambio de archivos en el lugar como lo sedhace.
Me gustaría usar sedpara no tener que reinventar la rueda de malabares con los archivos temporales.

n.st
fuente
Responde esta pregunta por cuenta propia, ya que parece que no pude encontrar ningún resultado para "caracteres de traducción sed". La palabra clave mágica terminó siendo "transliterar", pero pensé que valía la pena hacer que esta función fuera lo más fácil de encontrar posible.
n.st
Algo a tener en cuenta al intentar implementar soluciones para esto: tr(correctamente) ignora la recursividad en los conjuntos de reemplazo: echo 'abc' | tr ab bxbxc. Una solución primitiva podría hacerlo xxcporque vuelve a aplicar la traducción a los caracteres que ya se han traducido.
n.st
Relacionado: tr analógico para caracteres unicode? (GNU sedcontrario a GNU trpuede transliterar caracteres de varios bytes)
Stéphane Chazelas
Si desea otra posibilidad: perl puede traducir, y -i y (a menos que sea antiguo) multibyte. No POSIX, pero bastante común.
dave_thompson_085

Respuestas:

24

sedtiene el ycomando que funciona igual que tr:

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

El ycomando es parte de la especificación POSIXsed , por lo que debería funcionar en casi cualquier plataforma.

Y dado que es así sed, puede hacer que reemplace un archivo con su versión editada, ahorrándole el molesto negocio de los archivos temporales (siempre que su implementación de sea sedcompatible con la -iopción, que no está especificada por POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt
n.st
fuente
@ StéphaneChazelas Gracias por señalarlo; No estaba al tanto del funcionamiento interno hasta ahora. He editado mi respuesta para mencionar eso.
n.st
¡Gracias, esto es extraordinariamente útil! Esperaba que funcionara en VIM (8.0.1092 en CentOS 7.3) pero no funciona. ¿No debería hacer nada sed, lo hace VIM?
dotancohen
1
@dotancohen El hecho de que la función de sustitución de Vim esté modelada después de sedla de s no significa que las otras funciones también lo estén. ;) La lista de correo de Vim tiene un hilo sobre encontrar un y/abc/def/equivalente; La mejor opción parece ser :%call setline(".", tr(getline("."),"abc","def")).
n.st
8

Si, como en su caso, está transcribiendo caracteres sin cambiar su tamaño (de todos modos, algunas implementaciones como GNU trsolo admiten caracteres de un solo byte), puede hacer:

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

Es decir, trsobrescribir el archivo sobre sí mismo.

Eso es mejor que sed -ien varias cuentas:

  • no necesita espacio adicional en el disco (a excepción de algunos archivos dispersos, casos especiales de copia en escritura)
  • conserva números de inodo, propiedad, permisos, ACL ...
  • funciona bien con enlaces simbólicos, no rompe enlaces duros
  • no deja archivos temporales sobre cuando se mata.

Un inconveniente es que si se interrumpe, el archivo terminará siendo medio traducido (en este caso, sin embargo, puede ejecutarlo nuevamente para finalizarlo). Algunas sedimplementaciones manejarían eso correctamente asegurándose de que el archivo original permanezca sin cambios a menos que el comando tenga éxito.

Stéphane Chazelas
fuente
3
Tenga cuidado de volver a ejecutar la traducción si tiene recurrencia en los conjuntos de traducción, por ejemplo echo 'abc' | tr ab bx.
n.st
1
@ n.st, sí, por eso dije en este caso , aunque estoy de acuerdo en que vale la pena explicarlo.
Stéphane Chazelas
Al final, después de todo, tuve que trabajar con archivos temporales: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - Los caracteres multibyte hicieron imposible usar GNU try en nuestro entorno PXE con muchos enlaces simbólicos, sed -iera una espera de mierda sucederá ...: /
n.st
@ n.st, iconv -t cp437parece más apropiado para eso.
Stéphane Chazelas
iconvse rompe cuando el archivo de entrada ya contiene bytes codificados con cp437, o una mezcla de codificaciones múltiples. Entonces, aunque es preferible en el caso general, es más robusto hacer reemplazos manuales en este caso.
n.st
4

Como otra alternativa, si su problema principal es la falta de soporte para cambiar los archivos en el lugar, es posible que le interese la spongeherramienta del paquete moreutils :

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

escribirá en file, pero solo se abrirá filepara escribir una vez que se complete la entrada. Desde la página del manual :

spongelee la entrada estándar y la escribe en el archivo especificado. A diferencia de un redireccionamiento de shell, la esponja absorbe toda su entrada antes de abrir el archivo de salida. Esto permite construir tuberías que leen y escriben en el mismo archivo.

A menos que tenga archivos realmente grandes que no se pueden guardar en la memoria, spongepodría funcionar para usted.

mindriot
fuente
2
Uno de los problemas spongees que todavía se sobrescribe filesi trfalla (por ejemplo, si tenía acceso de escritura pero no de lectura file)
Stéphane Chazelas
Oh, de hecho lo hace; No esperaba eso. Gracias.
mindriot
Vea el cat file >; fileoperador de ksh93 que escribe la salida en un archivo temporal que se renombra al destino solo si el comando tiene éxito (pero sed -i, como , eso crea un nuevo archivo en lugar de sobrescribir el original).
Stéphane Chazelas