¿Se pueden encadenar los comandos tr para evitar múltiples procesos tr en una tubería?

11

Tengo un montón de archivos txt, me gustaría mostrarlos en minúsculas, solo alfabéticos y una palabra por línea, puedo hacerlo con varios trcomandos en una tubería como esta:

tr -d '[:punct:]' <doyle_sherlock_holmes.txt | tr '[:upper:]' '[:lower:]' | tr ' ' '\n'

¿Es posible hacer esto en un escaneo? Podría escribir un programa en C para hacer esto, pero me siento como si hubiera una manera de hacerlo utilizando tr, sed, awko perl.

tlehman
fuente
¿Qué sistema operativo estás usando? ¿Tienes acceso a las herramientas de GNU?
terdon

Respuestas:

9

Puede combinar varias traducciones (excepto casos complejos que involucran conjuntos superpuestos dependientes de la configuración regional), pero no puede combinar la eliminación con la traducción.

<doyle_sherlock_holmes.txt tr -d '[:punct:]' | tr '[:upper:] ' '[:lower:]\n'

trEs probable que dos llamadas sean más rápidas que una sola llamada a herramientas más complejas, pero esto depende mucho del tamaño de entrada, de las proporciones de los diferentes caracteres, de la implementación try de las herramientas de la competencia, del sistema operativo, del número de núcleos, etc.

Gilles 'SO- deja de ser malvado'
fuente
No estoy seguro de volver a combinartr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas
1
@Costas Eso transformaría la puntuación en nuevas líneas. Puede estar bien para esta aplicación en particular, pero el resultado no es el mismo que el original.
Gilles 'SO- deja de ser malvado'
@Costas: aunque lo de la nueva línea podría ser aceptable aquí, no creo que exprimir los caracteres en mayúscula lo sea. Por ejemplo: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'gets a\na\na', y la transformación para ... '[:lower:]\n'podría no necesariamente hacer nada de todos '[:punct:]'modos: algunos trs truncarán set1 para que coincida con 2 y algunos harán un implícito [\n*]. Es mejor usar el rango allí.
mikeserv
4

Aquí hay algunos enfoques:

  • GNU grepy tr: encuentra todas las palabras y en minúsculas

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep y perl: como arriba pero perl maneja la conversión a minúsculas

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: encuentre todos los caracteres alfabéticos e imprímalos en minúsculas (gracias @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: elimine todos los caracteres que no sean alfabéticos o espacios, sustituya todos los caracteres alfabéticos con sus versiones en minúsculas y reemplace todos los espacios con líneas nuevas. Tenga en cuenta que esto supone que todos los espacios en blanco son espacios, no pestañas.

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file
terdon
fuente
2
¿Algo así perl -lne 'print lc for /[[:alpha:]]+/g'también funcionaría? o es de mal estilo? (¡Soy nuevo en Perl e intento aprender!)
steeldriver
@steeldriver sí lo haría, agradable! Si está aprendiendo Perl, estoy seguro de que ha encontrado su lema: TMTOWTDI :) Gracias, lo agregaré .
terdon
3
Con nueva versión (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas
@Costas ah, ¿ sedpuedes hacer \wahora? ¡Frio!
terdon
@terdon - se hace que por un tiempo, pero, debido a Costas no lo mencionó, creo que la cosa más interesante sobre el comentario anterior es GNU sed's -zero delimitan interruptor - que los ciclos más de \0NULs en lugar de saltos de línea. Bastante genial cuando haces algo como tar -c . | tr -s \\0 | sed -z ..., pero un poco lento.
mikeserv
4

Si. Puede hacer eso con truna configuración regional ASCII (que es, para un GNU de trtodos modos, algo así como su único alcance) . Puede usar las clases POSIX, o puede hacer referencia a los valores de bytes de cada carácter por número octal. También puede dividir sus transformaciones en rangos.

LC_ALL=C tr '[:upper:]\0-\101\133-140\173-\377' '[:lower:][\n*]' <input

El comando anterior transformaría todos los caracteres en mayúsculas a minúsculas, ignoraría por completo los caracteres en minúsculas y transformaría todos los demás caracteres a nuevas líneas. Por supuesto, entonces terminas con un montón de líneas en blanco. Las tr -srepeticiones queeze detector puede resultar útil en ese caso, pero si lo usa junto con el [:upper:]de [:lower:]la transformación, entonces terminan apretando los caracteres en mayúsculas también. De esa manera, todavía requiere un segundo filtro como ...

LC... tr ... | tr -s \\n

...o...

LC... tr ... | grep .

... y entonces resulta ser mucho menos conveniente que hacerlo ...

LC_ALL=C tr -sc '[:alpha:]' \\n <input | tr '[:upper:]' '[:lower:]'

... que exprime la -csecuencia de caracteres alfabéticos por secuencia en una sola línea nueva de una pieza, luego hace la transformación de arriba a abajo en el otro lado de la tubería.

Eso no quiere decir que los rangos de esa naturaleza no sean útiles. Cosas como:

tr '\0-\377' '[1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][0*]' </dev/random

... puede ser bastante útil ya que convierte los bytes de entrada a todos los dígitos en un amplio espectro de sus valores. No desperdicies, no quieras, ya sabes.

Otra forma de hacer la transformación podría involucrar dd.

tr '\0-\377' '[A*64][B*64][C*64][D*64]' </dev/urandom |
dd bs=32 cbs=8 conv=unblock,lcase count=1

dadbbdbd
ddaaddab
ddbadbaa
bdbdcadd

Debido a que ddpuede hacer ambas cosas unblocky lcaseconversiones al mismo tiempo, incluso podría ser posible pasarle gran parte del trabajo. Pero eso solo puede ser realmente útil si puede predecir con precisión el número de bytes por palabra, o al menos puede rellenar cada palabra con espacios de antemano a un recuento de bytes predecible, porque unblockcome espacios finales al final de cada bloque.

mikeserv
fuente
+2 puntos de bonificación por ddparticipar :)
tlehman
@TobiLehman - Estoy muy contento de que lo apruebes.
mikeserv