repetitions regular plus pattern one multiplicity more many lazy characters and regex sed pcre greedy regex-greedy

regex - plus - No ávido(reacio) emparejamiento de expresiones regulares en sed?



regex repetitions (20)

Estoy tratando de usar sed para limpiar líneas de URL para extraer solo el dominio ...

Así que desde

http://www.suepearson.co.uk/product/174/71/3816/

Quiero:

http://www.suepearson.co.uk/

(ya sea con o sin la barra de entrenamiento, no importa)

Yo he tratado:

sed ''s|/(http:////.*?///).*|/1|''

y (escapando del cuantificador no codicioso)

sed ''s|/(http:////.*/?///).*|/1|''

pero parece que no consigo que el cuantificador no codicioso funcione, por lo que siempre termina coincidiendo con toda la cadena.


Simulando un cuantificador perezoso (sin codicia) en sed

¡Y todos los demás sabores regex!

  1. Encontrando la primera aparición de una expresión:

    • POSIX ERE (usando la opción -r )

      Regex:

      (EXPRESSION).*|.

      Sed:

      sed -r "s/(EXPRESSION).*|.//1/g" # Global `g` modifier should be on

      Ejemplo (encontrando la primera secuencia de dígitos) Demostración en vivo :

      $ sed -r "s/([0-9]+).*|.//1/g" <<< "foo 12 bar 34"

      12

      ¿Cómo funciona ?

      Esta expresión regular se beneficia de una alternancia | . En cada posición, el motor buscará el primer lado de la alternancia (nuestro objetivo) y si no coincide con el segundo lado de la alternación que tiene un punto . coincide con el siguiente carácter inmediato.

      Dado que el indicador global está establecido, el motor intenta continuar uniendo carácter por carácter hasta el final de la cadena de entrada o nuestro destino. Tan pronto como el primer y único grupo de captura del lado izquierdo de la alternancia coincida (EXPRESSION) resto de la línea también se consumirá de inmediato .* . Ahora tenemos nuestro valor en el primer grupo de captura.

    • POSIX BRE

      Regex:

      /(/(/(EXPRESSION/).*/)*./)*

      Sed:

      sed "s//(/(/(EXPRESSION/).*/)*./)*//3/"

      Ejemplo (encontrando la primera secuencia de dígitos):

      $ sed "s//(/(/([0-9]/{1,/}/).*/)*./)*//3/" <<< "foo 12 bar 34"

      12

      Esta es como la versión ERE pero sin alternancia involucrada. Eso es todo. En cada posición única, el motor intenta hacer coincidir un dígito.

      Si se encuentra, otros dígitos siguientes se consumen y se capturan y el resto de la línea se compara inmediatamente de lo contrario, ya que * significa más o cero, se omite en el segundo grupo de captura /(/([0-9]/{1,/}/).*/)* y llega a un punto . para coincidir con un solo personaje y este proceso continúa.

  2. Encontrando la primera aparición de una expresión delimitada :

    Este enfoque coincidirá con la primera aparición de una cadena delimitada. Podemos llamarlo bloque de cuerda.

    sed "s//(END-DELIMITER-EXPRESSION/).*//1/; / s//(/(START-DELIMITER-EXPRESSION.*/)*./)*//1/g"

    Cadena de entrada:

    foobar start block #1 end barfoo start block #2 end

    -EDE: end

    -SDE: start

    $ sed "s//(end/).*//1/; s//(/(start.*/)*./)*//1/g"

    Salida:

    start block #1 end

    First regex /(end/).* Compara y captura el primer final del delimitador end y sustituye todas las coincidencias con los caracteres capturados recientes, que es el delimitador final. En esta etapa nuestra salida es: foobar start block #1 end .

    Luego, el resultado se pasa a la segunda expresión regular /(/(start.*/)*./)* Que es igual a la versión POSIX BRE anterior. Coincide con un solo carácter si el inicio del delimitador de start no coincide, de lo contrario coincide y captura el delimitador de inicio y el resto de caracteres.

Respondiendo directamente a tu pregunta

Usando el enfoque # 2 (expresión delimitada) debe seleccionar dos expresiones apropiadas:

  • EDE: [^:/]//

  • SDE: http:

Uso:

$ sed "s//([^:/]///).*//1/g; s//(/(http:.*/)*./)*//1/" <<< "http://www.suepearson.co.uk/product/174/71/3816/"

Salida:

http://www.suepearson.co.uk/


Solución no codiciosa para más de un solo personaje.

Este hilo es realmente viejo pero supongo que la gente todavía lo necesita. Digamos que quieres matar todo hasta la primera aparición de HELLO . No puedes decir [^HELLO] ...

Por lo tanto, una buena solución implica dos pasos, suponiendo que puede ahorrar una palabra única que no espera en la entrada, digamos top_sekrit .

En este caso podemos:

s/HELLO/top_sekrit/ #will only replace the very first occurrence s/.*top_sekrit// #kill everything till end of the first HELLO

Por supuesto, con una entrada más simple podría usar una palabra más pequeña, o incluso un solo carácter.

HTH!


Aquí hay algo que puede hacer con un enfoque de dos pasos y awk:

A=http://www.suepearson.co.uk/product/174/71/3816/ echo $A|awk '' { var=gensub(///,"||",3,$0) ; sub(//|/|.*/,"",var); print var }''

Salida: http://www.suepearson.co.uk/

¡Espero que ayude!


Ciertamente tiene su lugar pero este no es uno de ellos!

Como ha señalado Dee: Sólo usa el cut . Es mucho más simple y mucho más seguro en este caso. Aquí hay un ejemplo donde extraemos varios componentes de la URL usando la sintaxis de Bash:

url="http://www.suepearson.co.uk/product/174/71/3816/" protocol=$(echo "$url" | cut -d'':'' -f1) host=$(echo "$url" | cut -d''/'' -f3) urlhost=$(echo "$url" | cut -d''/'' -f1-3) urlpath=$(echo "$url" | cut -d''/'' -f4-)

te dio:

protocol = "http" host = "www.suepearson.co.uk" urlhost = "http://www.suepearson.co.uk" urlpath = "product/174/71/3816/"

Como pueden ver, este es un enfoque mucho más flexible.

(todo el crédito a Dee)


Como específicamente dijo que está tratando de usar sed (en lugar de perl, cortar, etc.), intente agrupar. Esto evita que el identificador no codicioso no sea reconocido. El primer grupo es el protocolo (es decir, ''http: //'', ''https: //'', ''tcp: //'', etc.). El segundo grupo es el dominio:

echo "http://www.suon.co.uk/product/1/7/3/" | sed "s|^/(.*///)/([^/]*/).*$|/1/2|"

Si no está familiarizado con la agrupación, comience here .


Con sed, por lo general implemento una búsqueda no codiciosa buscando cualquier cosa excepto el separador hasta el separador:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n ''s;/(http://[^/]*/)/.*;/1;p''

Salida:

http://www.suon.co.uk

esto es:

  • no salir -n
  • buscar, hacer coincidir patrón, reemplazar e imprimir s/<pattern>/<replace>/p
  • utilizar busque el separador de comando en lugar de / para que sea más fácil escribirlo s;<pattern>;<replace>;p
  • recuerda la coincidencia entre corchetes /( ... /) , posteriormente accesible con /1 , /2 ...
  • coincide con http://
  • seguido de cualquier cosa entre corchetes [] , [ab/] significaría a o b o /
  • primero ^ en [] significa not , por lo tanto seguido de cualquier cosa que no sea la cosa en []
  • entonces [^/] significa cualquier cosa excepto / carácter
  • * es para repetir el grupo anterior, por lo que [^/]* significa caracteres excepto / .
  • hasta ahora sed -n ''s;/(http://[^/]*/) significa buscar y recordar http:// seguido de cualquier carácter excepto / y recuerda lo que has encontrado
  • queremos buscar hasta el final del dominio, así que pare en el siguiente / agregue otro / al final: sed -n ''s;/(http://[^/]*/)/'' pero queremos coincidir con el Resto de la línea después del dominio, así que añada .*
  • ahora la coincidencia recordada en el grupo 1 ( /1 ) es el dominio, así que reemplace la línea coincidente con las cosas guardadas en el grupo /1 e imprima: sed -n ''s;/(http://[^/]*/)/.*;/1;p''

Si también desea incluir la barra invertida después del dominio, agregue una barra invertida más en el grupo para recordar:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n ''s;/(http://[^/]*//).*;/1;p''

salida:

http://www.suon.co.uk/


Esta es la forma de hacer un emparejamiento no codicioso de cadenas de múltiples caracteres utilizando sed. Digamos que desea cambiar cada foo...bar a <foo...bar> así que, por ejemplo, esta entrada:

$ cat file ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV

Debería convertirse esta salida:

ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

Para hacer eso, convierte foo y bar en caracteres individuales y luego usa la negación de esos caracteres entre ellos:

$ sed ''s/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g'' file ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

En lo anterior:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/g s/@/@A/g; s/{/@B/g; s/}/@C/g está convirtiendo { y } en cadenas de marcador de posición que no pueden existir en la entrada, por lo que esos caracteres están disponibles para convertir foo y bar a.
  2. s/foo/{/g; s/bar/}/g s/foo/{/g; s/bar/}/g está convirtiendo foo y bar a { y } respectivamente
  3. s/{[^{}]*}/<&>/g está realizando la operación que queremos: convertir foo...bar a <foo...bar>
  4. s/}/bar/g; s/{/foo/g s/}/bar/g; s/{/foo/g está convirtiendo { y } nuevo a foo y bar .
  5. s/@C/}/g; s/@B/{/g; s/@A/@/g s/@C/}/g; s/@B/{/g; s/@A/@/g está convirtiendo las cadenas de marcador de posición de nuevo a sus caracteres originales.

Tenga en cuenta que lo anterior no depende de que ninguna cadena en particular esté presente en la entrada, ya que fabrica dichas cadenas en el primer paso, ni le importa qué ocurrencia de cualquier expresión regular en particular desea hacer coincidir ya que puede usar {[^{}]*} tantas veces como sea necesario en la expresión para aislar la coincidencia real que desea y / o con el operador de coincidencia numérica seds, por ejemplo, para reemplazar solo la segunda aparición:

$ sed ''s/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g'' file ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV


Esto se puede hacer usando corte:

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d''/'' -f1-3


La expresión regular de Posix / GNU básica ni extendida no reconoce el cuantificador no codicioso; necesita un regex posterior Afortunadamente, la expresión regular de Perl para este contexto es bastante fácil de obtener:

perl -pe ''s|(http://.*?/).*|/1|''


Me doy cuenta de que esta es una entrada antigua, pero a alguien le puede resultar útil. Como el nombre de dominio completo no puede exceder una longitud total de 253 caracteres, reemplace. * Con. / {1, 255 /}


Otra forma, sin usar expresiones regulares, es usar campos / delimitadores, por ejemplo.

string="http://www.suepearson.co.uk/product/174/71/3816/" echo $string | awk -F"/" ''{print $1,$2,$3}'' OFS="/"


Otra versión sed:

sed ''s|/[:alphanum:].*||'' file.txt

Coincide con / seguido de un carácter alfanumérico (por lo que no es otra barra diagonal), así como el resto de caracteres hasta el final de la línea. Luego lo reemplaza con nada (es decir, lo elimina).


Pruebe [^/]* lugar de .*? :

sed ''s|/(http://[^/]*//).*|/1|g''


Todavía hay esperanza de resolver esto usando pure (GNU) sed. A pesar de que esto no es una solución genérica, en algunos casos puedes usar "bucles" para eliminar todas las partes innecesarias de la cadena como esta:

sed ''s|(http:////[^//]+//).*|/1|''

  • -r: usar expresiones regulares extendidas (para + y paréntesis no escapados)
  • ": loop": define una nueva etiqueta llamada "loop"
  • -e: agregar comandos a sed
  • "t loop": salta de vuelta a la etiqueta "loop" si hubo una sustitución exitosa

El único problema aquí es que también cortará el último carácter separador (''/''), pero si realmente lo necesita, puede simplemente volver a ponerlo cuando finalice el "bucle", simplemente agregue este comando adicional al final del anterior. línea de comando:

sed -r -e ":loop" -e ''s|(http://.+)/.*|/1|'' -e "t loop"


sed -E interpreta expresiones regulares como expresiones regulares extendidas (modernas)

Actualización: -E en MacOS X, -r en GNU sed.


sed no es compatible con el operador "no codicioso".

Debe utilizar el operador "[]" para excluir "/" de la coincidencia.

sed ''s,/(http://[^/]*/)/.*,/1,''

PD: no es necesario hacer una barra invertida "/".


sed - emparejamiento no codicioso por Christoph Sieghart

El truco para obtener una coincidencia no codiciosa en sed es hacer coincidir todos los caracteres, excepto el que termina la coincidencia. Lo sé, no lo creo, pero desperdicié unos minutos preciosos y los scripts de shell deberían ser, después de todo, rápidos y fáciles. Así que en caso de que alguien más lo necesite:

Coincidencia codiciosa

% echo "<b>foo</b>bar" | sed ''s/<.*>//g'' bar

Emparejamiento no codicioso

% echo "<b>foo</b>bar" | sed ''s/<[^>]*>//g'' foobar


sed ''s|/(http:////www/.[az.0-9]*///).*|/1| trabaja tambien


-e "s,$,/,"


echo "/home/one/two/three/myfile.txt" | sed ''s|/(.*/)/.*|/1|''

No te molestes, lo tengo en otro foro :)