regulares expresiones regex linux shell

regex - expresiones regulares linux



Usando expresiones regulares en shell script (11)

¿Cuál es la forma correcta de analizar una cadena usando expresiones regulares en un script de shell de Linux? Escribí la siguiente secuencia de comandos para imprimir mi representante de SO en la consola usando curl y sed (no solo porque estoy loca por la representación; estoy tratando de aprender algunos scripts de shell y expresiones regulares antes de cambiar a Linux).

json=$(curl -s http://stackoverflow.com/users/flair/165297.json) echo $json | sed ''s/.*"reputation":"/([0-9,]/{1,/}/)".*//1/'' | sed s/,//

Pero de alguna manera siento que sed no es la herramienta adecuada para usar aquí. Escuché que grep tiene que ver con expresiones regulares y lo exploré un poco. Pero, al parecer, imprime toda la línea cada vez que se encuentra una coincidencia: estoy intentando extraer un número de una sola línea de texto. Aquí hay una versión reducida de la cadena en la que estoy trabajando (devuelto por curl ).

{"displayName": "Amarghosh", "reputación": "2,737", "badgeHtml": "/ u003cspan title = /" 1 insignia de plata / "/ u003e / u003cspan class = /" badge2 / "/ u003e & # 9679; / u003c / span / u003e / u003cspan class = / "badgecount /" / u003e1 / u003c / span / u003e / u003c / span / u003e "}

Supongo que mis preguntas son:

  • ¿Cuál es la forma correcta de analizar una cadena usando expresiones regulares en un script de shell de Linux?
  • ¿Es lo correcto usar aquí?
  • ¿Podría hacerse esto usando grep ?
  • ¿Hay algún otro comando que sea más fácil / apropiado?

1) ¿Cuál es la forma correcta de analizar una cadena usando expresiones regulares en un script de shell de Linux?

Las herramientas que incluyen capacidades de expresión regular incluyen sed, grep, awk, Perl, Python, por mencionar algunas. Incluso la versión más reciente de Bash tiene capacidades de expresiones regulares. Todo lo que necesita hacer es buscar los documentos sobre cómo usarlos.

2) ¿Es lo correcto usar aquí?

Puede ser, pero no es necesario.

3) ¿Podría hacerse esto usando grep?

Sí puede. simplemente construirá expresiones regulares similares a como lo haría si usara sed, u otros. Tenga en cuenta que grep solo hace lo que hace, y si desea modificar cualquier archivo, no lo hará por usted.

4) ¿Hay algún otro comando que sea más fácil / más apropiado?

Por supuesto. Las expresiones regulares pueden ser poderosas, pero no es necesariamente la mejor herramienta para usar en todo momento. También depende de lo que quieras decir con "más fácil / apropiado". El otro método para usar con un mínimo esfuerzo en las expresiones regulares es usar el enfoque de campos / delimitadores. busca patrones que puedan ser "divididos". por ejemplo, en su caso (he descargado el archivo 165297.json en lugar de usar curl .. (pero es lo mismo)

awk ''BEGIN{ FS="reputation" # split on the word "reputation" } { m=split($2,a,"/",/"") # field 2 will contain the value you want plus the rest # Then split on ":" and save to array "a" gsub(/[:/",]/,"",a[1]) # now, get rid of the redundant characters print a[1] }'' 165297.json

salida:

$ ./shell.sh 2747


RegEx simple a través de Shell

Sin tener en cuenta el código específico en cuestión, puede haber ocasiones en las que desee realizar una rápida expresión regular de reemplazo de todos los stdin a stdout usando shell, de una manera sencilla, utilizando una sintaxis de cadena similar a JavaScript.

A continuación hay algunos ejemplos para cualquiera que busque una manera de hacer esto. Perl es una mejor apuesta para Mac, ya que carece de algunas opciones de sed. Si desea obtener stdin como una variable, puede usar MY_VAR=$(cat); .

echo ''text'' | perl -pe ''s/search/replace/g''; # using perl
echo ''text'' | sed -e ''s/search/replace/g''; # using sed

Y aquí hay un ejemplo de una función de expresión regular reutilizable y personalizada. Los argumentos son cadena de origen (o - para stdin), búsqueda , reemplazo y opciones .

regex() { case "$#" in ( ''0'' ) exit 1 ;; ( ''1'' ) echo "$1"; exit 0 ;; ( ''2'' ) REP='''' ;; ( ''3'' ) REP="$3"; OPT='''' ;; ( * ) REP="$3"; OPT="$4" ;; esac TXT="$1"; SRCH="$2"; if [ "$1" = "--" ]; then [ ! -t 0 ] && read -r TXT; fi echo "$TXT" | perl -pe ''s/''"$SRCH"''/''"$REP"''/''"$OPT"; }

echo ''text'' | regex -- search replace g;


A ciegas:

echo $json | awk -F/" ''{print $8}''

Similar (el separador de campo puede ser una expresión regular):

awk -F''{"|":"|","|"}'' ''{print $5}''

Más inteligente (busque la clave e imprima su valor):

awk -F''{"|":"|","|"}'' ''{for(i=2; i<=NF; i+=2) if ($i == "reputation") print $(i+1)}''


El comando grep seleccionará la (s) línea (s) deseada (s) de muchas, pero no manipulará directamente la línea. Para eso, usas sed en una tubería:

someCommand | grep ''Amarghosh'' | sed -e ''s/foo/bar/g''

Alternativamente, se puede usar awk (o perl si está disponible). En mi opinión, es una herramienta de procesamiento de texto mucho más poderosa que sed .

someCommand | awk ''/Amarghosh/ { do something }''

Para manipulaciones de texto simples, simplemente quédate con el combo grep/sed . Cuando necesite un procesamiento más complicado, awk con awk o perl .

Mi primer pensamiento es simplemente usar:

echo ''{"displayName":"Amarghosh","reputation":"2,737","badgeHtml"'' | sed -e ''s/.*tion":"//'' -e ''s/".*//'' -e ''s/,//g''

que mantiene el número de procesos sed en uno (puede dar varios comandos con -e ).


Mi proposición:

$ echo $json | sed ''s/,//g;s/^.*reputation.../([0-9]*/).*$//1/''

Pongo dos comandos en el argumento sed:

  • s/,//g se utiliza para eliminar todas las comas, en particular las que están presentes en el valor de reputación.

  • s/^.*reputation.../([0-9]*/).*$//1/ localiza el valor de reputación en la línea y reemplaza toda la línea por ese valor.

En este caso particular, encuentro que sed proporciona el comando más compacto sin pérdida de legibilidad.

Otras herramientas para manipular cadenas (no solo expresiones regulares) incluyen:

  • grep , awk , perl mencionado en la mayoría de las otras respuestas
  • tr para reemplazar personajes
  • cut , paste para manejar entradas multicolumnas
  • bash con su sintaxis $(...) para acceder a las variables
  • tail , head para guardar las últimas o primeras líneas de un archivo.

Para trabajar con JSON en el script de shell, use jsawk que le gusta awk, pero para JSON .

json=$(curl -s http://.com/users/flair/165297.json) echo $json | jsawk ''return this.reputation'' # 2,747


Puede estar interesado en usar Perl para tales tareas. A modo de demostración, aquí hay un script de Perl que imprime el número que desea:

#!/usr/local/bin/perl use warnings; use strict; use LWP::Simple; use JSON; my $url = "http://.com/users/flair/165297.json"; my $flair = get ($url); my $parsed = from_json ($flair); print "$parsed->{reputation}/n";

Esta secuencia de comandos requiere que instale el módulo JSON, lo que puede hacer con solo el comando cpan JSON .


Puede utilizar una biblioteca adecuada (como han señalado otros):

E:/Home> perl -MLWP::Simple -MJSON -e "print from_json(get ''http://.com/users/flair/165297.json'')->{reputation}"

o

$ perl -MLWP::Simple -MJSON -e ''print from_json(get "http://.com/users/flair/165297.json")->{reputation}, "/n"''

Dependiendo de la combinación de sistema operativo / shell.


Puedes hacerlo con grep. Hay un modificador -o en grep que extrae solo la cadena coincidente, no la línea completa.

$ echo $json | grep -o ''"reputation":"[0-9,]/+"'' | grep -o ''[0-9,]/+'' 2,747


sed es apropiado, pero generará un nuevo proceso para cada sed que use (que puede ser demasiado pesado en escenarios más complejos). grep no es realmente apropiado Es una herramienta de búsqueda que usa expresiones regulares para encontrar líneas de interés.

Perl es una solución adecuada aquí, ya que es un lenguaje de shell scripting con potentes funciones de expresión regular. Hará casi todo lo que necesita sin generar procesos separados (a diferencia de los scripts de shell de Unix normales) y tiene una enorme biblioteca de funciones adicionales.


sed es un comando perfectamente válido para su tarea, pero puede que no sea el único.

grep puede ser útil también, pero como dices, imprime toda la línea. Es más útil para filtrar las líneas de un archivo multilínea y descartar las líneas que no desea.

Los shell scripts eficientes pueden usar una combinación de comandos (no solo los dos que mencionaste), explotando los talentos de cada uno.