Manipulación de cadenas de bash

9

He leído algunas otras preguntas sobre manipulación de cadenas de bash de tuberías, pero parecen ser aplicaciones especializadas.

Básicamente, ¿hay alguna manera de hacer lo siguiente más simple?

en vez de

$ string='hello world'; string2="${string// /_}"; echo "${string2^^}"
HELLO_WORLD

algo como

$ echo 'hello world' | $"{-// /_}" | "${ -^^}"
HELLO_WORLD

Editar Estoy interesado en permanecer dentro de las manipulaciones de bash si es posible para mantener la velocidad (a diferencia de sed / awk, que tienden a ralentizar mis scripts)

Edit2: @jimmij

Me gusta el segundo ejemplo y me llevó a hacer una función.

bash_m() { { read x; echo "${x// /_}"; } | { read x; echo "${x^^}"; }; }
echo hello world | bash_m
HELLO_WORLD
Miati
fuente
1
¿Por qué crees que sed / awk sería lento para este propósito? Son tan rápidos como vienen.
mkc
1
@Ketan Sed y awk son procesos separados, por lo que nunca pueden ser rápidos como algo que bash puede hacer de forma nativa sin iniciar un proceso separado. Normalmente, esta diferencia apenas se nota, pero cuando el rendimiento importa en los scripts de shell suele ser un cierto bucle o el cálculo se repite una gran cantidad de veces, y generar miles de procesos será notablemente más lento que hacer una simple manipulación de cadenas en bash de forma nativa.
jw013
2
@ jw013 Esto es cierto para cadenas cortas como "hola mundo" de la pregunta, pero si la cadena es muy larga, diga el trmanual, entonces lo contrario es cierto porque el tiempo de generación de los procesos es insignificante en comparación con el tiempo de manipulación de la cadena para la cual sedy awkson dedicados Si la cadena es extremadamente larga, diga todo el manual de bash, entonces bash puede negarse a continuar por completo, debido a algunas limitaciones internas.
jimmij
2
@ jw013 estoy reclamando código de manipulación de cadenas de fiesta que es menos eficientes herramientas dedicadas a continuación, como sed, awk, tro similares. Mire la respuesta gena2x, que edité hace algún tiempo agregando exactamente esta información: unix.stackexchange.com/questions/162221/... es posible que desee compararla con la respuesta terdon a la misma pregunta en la que da tiempo para cadenas cortas en las que el desove del proceso del caso lleva más tiempo. Puede probarlo usted mismo y publicar el resultado.
jimmij
1
@Miati ¿Por qué crees que este extra read x; echo $xes mejor para el rendimiento? La sintaxis no se ve más corta ni más limpia. x=${x// /_}; x=${x^^}es una forma mucho más concisa de hacer lo mismo que {read x; echo ${x.... En cuanto al rendimiento, @jimmij ha señalado que tr/ sedsería más rápido que bash, el recuento de horquillas es igual. Usar una tubería siempre resulta en un proceso adicional, por lo que el argumento de guardar una bifurcación ya no se aplica. Por lo tanto, si está utilizando tuberías, simplemente use sed/ tretc. Si puede hacerlo en bash, hágalo y omita estas read x; echo $xtonterías.
jw013

Respuestas:

9

Lo que dijo jimmij. Su último ejemplo es lo más cercano que puede llegar a lo que está intentando en su expresión canalizada.

Aquí hay una variante sobre ese tema:

echo 'hello world'|echo $(read s;s=${s^^};echo ${s// /_})

Me inclinaría a usar tr, ya que es bastante rápido.

echo 'hello world'|tr ' [:lower:]' '_[:upper:]'

Supongo que es una pena que bash no permita la expansión de parámetros anidados; OTOH, el uso de tales expresiones anidadas podría conducir fácilmente a un código que es doloroso de leer. A menos que realmente necesite que las cosas se ejecuten lo más rápido posible, es mejor escribir código que sea fácil de leer, comprender y mantener, en lugar de un código inteligente que es un PITA para depurar. Y si realmente necesita hacer las cosas a la máxima velocidad, debería usar código compilado, no un script.

PM 2Ring
fuente
7

No puede pasar expansiones de parámetros de esa manera. Cuando se refiere al xuso del $símbolo como en el "${x}"formulario, debe ser un nombre de variable real, no una entrada estándar, al menos no en bash. En zshpuede realizar sustituciones de parámetros anidados de la siguiente manera:

$ x=''hello world'
$ echo ${${x// /_}:u}
HELLO_WORLD

(nota: :ues para zshlo mismo que ^^para bash)

No es posible anidar en bash y creo que lo que escribiste en cuestión es lo mejor que puedes obtener, pero si por alguna extraña razón necesitas involucrar tuberías en la ecuación, entonces puedes intentar esto:

$ echo 'hello world' | { read x; echo "${x// /_}"; } | { read y; echo "${y^^}"; }
HELLO_WORLD
jimmij
fuente
1
Teniendo en cuenta nuestra conversación reciente sobre cómo tr/ sedson más rápidos que bashen el procesamiento de cadenas, y considerando cómo está utilizando tuberías para pasar cadenas a través de E / S estándar, veo literalmente cero punto para hacer esas operaciones en bash en lugar de tr/ sed. ¿Por qué alguien en | { read x; echo $x... }lugar de uno | sedque hace lo mismo?
jw013
1
@ jw013 hablando con franqueza no veo casi ningún punto. Es sólo un ejemplo para participar enérgicamente tuberías para el problema, porque OP pidió expresamente para ellos y no quería usar programas externos (ambos echoy readson de bash muebles empotrados, por lo que, en principio, un poco más rápido). Como ya escribí en la respuesta, la manipulación progresiva de parámetros que OP tiene en la pregunta es lo mejor que puedo obtener en mi opinión para esta tarea en bash. De todos modos, el problema es bastante académico.
jimmij