Mejor manera de hacer "echo $ x | sed ... "y" echo $ x | grep ... "

14

A menudo encuentro esto en los scripts (y, debo admitirlo, escribirlo yo mismo):

a=$(echo "$x" | sed "s/foo/bar/")

o

if echo "$x" | grep -q foo
then
    ...
fi

Considere "foo" para incluir algunas cosas de expresiones regulares.

Siento que debería haber, y lo más probable es , una mejor manera de expresar esto, una que no implique dos comandos y una tubería, pero que envuelva la cosa en una expresión más compacta.

Simplemente no puedo encontrarlo. ¿Cualquiera?

DevSolar
fuente
Espero que esta expresión se use con frecuencia debido a una combinación de ignorancia (no conocer alternativas) y mantenibilidad (conocer alternativas pero elegir esto como el más simple de entender). Puedo decir de un vistazo lo que hacen sus ejemplos, pero necesito una referencia de shell para descubrir las alternativas en las respuestas de Grawity y Dan McG.
quack quijote
3
Por cierto, el método preferido para hacer la sustitución de comandos es en $()lugar de backticks.
Pausado hasta nuevo aviso.
2
También es una buena idea citar expansiones para proteger los espacios en blanco: a="$(echo "$x" | sed "s/foo/bar/")"y if echo "$x" | grep foo; ….
Chris Johnsen
Buenas observaciones sobre $ () vs. ``. Veo que mis habilidades de bash aún no son tan buenas.
DevSolar

Respuestas:

12

A menos que asuma un shell específico, no hay mejor manera de hacer esto que "eco de tubería a herramienta" (o simplemente una "herramienta" como expr ); es todo lo que realmente puede contar con el shell Bourne tradicional y / o el shell POSIX . Si considera otros proyectiles, existen otras posibilidades incorporadas.

ksh tiene

  • patrones adicionales: ?(pattern-list), *(pattern-list), {n}(pattern-list), {n,m}(pattern-list), @(pattern-list), !(pattern-list),
  • el especificador %P printf para convertir una expresión regular extendida en un patrón (y %Rpara una expresión regular extendida en patrón);
  • la expr == patterncondición en las [[ expr ]]pruebas;
  • La ${param/pattern/replacement}expansión del parámetro.

bash tiene

  • la extglobopción para habilitar la mayoría de los patrones adicionales de ksh (no {n}y {n,m});
  • la expr == patterncondición (en [[ expr ]]pruebas);
  • la ${param/pattern/replacement}expansión del parámetro;
  • (en versiones más recientes) la expr =~ extregexpcondición (en [[ expr ]]pruebas) que puede coincidir con expresiones regulares extendidas
    • con subexpresiones entre paréntesis y el BASH_REMATCHparámetro, se podrían hacer reemplazos de estilo sed .

zsh tiene

  • sus propios patrones extendidos con la EXTENDED_GLOBopción;
  • patrones extendidos tipo ksh con la KSH_GLOBopción;
  • la expr == patterncondición (en [[ expr ]]pruebas);
  • la ${pattern/pattern/replacement}expansión del parámetro;
  • la expr =~ extregexpcondición (en [[ expr ]]pruebas) que puede coincidir con expresiones regulares extendidas,
    • puede usar PCRE en lugar de expresiones regulares extendidas simples si la opción RE_MATCH_PCRE está establecida,
    • con subexpresiones entre paréntesis, el MATCHparámetro y el matchparámetro (o BASH_REMATCHcon el BASH_REMATCHconjunto de opciones), se podrían realizar reemplazos de estilo sed ;
  • el zsh/pcremódulo que ofrece pcre_compile, pcre_studyy pcre_matchcomandos y la condición de -pcre-match prueba (en [[ expr ]]pruebas);
  • El zsh/regexmódulo que ofrece la condición de -regex-match prueba (en [[ expr ]]pruebas).
Chris Johnsen
fuente
Guau. Tan completo como uno podría desear. Definitivamente un ganador.
DevSolar
6

Para reemplazar la línea sed, haga algo como

${a/foo/bar} o ${a//foo/bar}

En la primera forma, solo se reemplaza la primera instancia. La segunda forma es una búsqueda y reemplazo global.

En tu caso, sería

En lugar de:

if echo $x | grep foo
then
    ...
fi

Considere usar:

if [ $x =~ foo ]
then
    ...
fi

Donde fooes una expresión regular.

Dan McGrath
fuente
¿En qué conchas funciona esto?
Peltier
1
if [ $x =~ foo ]da un mensaje de error aquí. if [[ $x =~ foo ]]Sin embargo, funciona muy bien. ¡Gracias!
DevSolar
1
Todos estos son bashismos, requieren bash. Además, =~requiere bash> = 3 iirc.
phaphink
1
Iirc también, foo no es una expresión regular (como en PCRE) en estos ejemplos, pero usa comodines. Hay más de estas bondades de bash en la página de manual de bash, sección "Expansión de parámetros". Aprecio particularmente cosas como ${a##foo}y ${a%%bar}.
phaphink
3
El operador de coincidencia =~usa expresiones regulares. Esto es cierto, por ejemplo: [[ "abcdddde" =~ ^a.*d+.g* ]](eso es cero o más g's en lugar de g-wildcard, por ejemplo).
Pausado hasta nuevo aviso.
2

Una buena forma compatible posix para probar si una variable contiene un patrón es:

test ${var##*foo*} || <do something>;

La sintaxis de la expansión del parámetro es:

 ${parameter##pattern}

donde patternes un patrón de shell .

mrucci
fuente