No codicioso (reacio) coincidencia de expresiones regulares en sed?

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

Entonces desde:

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

Yo 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 puedo lograr que funcione el cuantificador no codicioso, por lo que siempre termina emparejando toda la cadena.

Ni la expresión regular Posix / GNU básica ni extendida reconoce el cuantificador no codicioso; necesitas una expresión regular posterior. Afortunadamente, Perl Regex para este contexto es bastante fácil de obtener:

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

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

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

Con sed, suelo implementar búsquedas no codiciosas 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 dar salida -n
  • buscar, combinar el patrón, reemplazar e imprimir s///p
  • uso ; Busque el separador de comandos en lugar de / para que sea más fácil escribir so s;;;p
  • recuerda la coincidencia entre corchetes \(\) , más tarde accesible con \1 , \2
  • coincidencia http://
  • seguido de cualquier cosa entre corchetes [] , [ab/] significaría a o b o /
  • primero ^ en [] significa not , seguido de cualquier cosa que no sea la cosa en []
  • entonces [^/] significa algo excepto / personaje
  • * es para repetir el grupo anterior así que [^/]* significa caracteres excepto / .
  • hasta el momento sed -n 's;\(http://[^/]*\) significa buscar y recordar http:// seguido de cualquier caracter excepto / y recordar lo que has encontrado
  • queremos buscar hasta el final del dominio así que deténgase en el siguiente / así que agregue otro / al final: sed -n 's;\(http://[^/]*\)/' pero queremos hacer coincidir el rest de la línea después del dominio así que agregue .*
  • ahora el partido recordado en el grupo 1 ( \1 ) es el dominio así que reemplace la línea coincidente con cosas guardadas en el grupo \1 e imprima: sed -n 's;\(http://[^/]*\)/.*;\1;p'

Si también desea incluir una 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/ 

sed no es compatible con el operador “no codicioso”.

Debe usar el operador “[]” para excluir “/” de la coincidencia.

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

PS no hay necesidad de barra invertida “/”.

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

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

De modo que una buena solución implica dos pasos, suponiendo que se puede ahorrar una palabra única que no se 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 tal vez incluso un solo carácter.

HTH!

Simulación de un cuantificador perezoso (no codicioso) en sed

¡Y todos los otros sabores de expresiones regulares!

  1. Encontrar 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 (encontrar 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 regex se beneficia de una alternancia | . En cada posición, el motor buscará el primer lado de la alternancia (nuestro objective) y si no coincide con el segundo lado de la alternancia que tiene un punto . coincide con el siguiente personaje inmediato.

      enter image description here

      Como se establece el indicador global, el motor intenta continuar emparejando carácter por carácter hasta el final de la cadena de entrada o nuestro objective. Tan pronto como coincida el primer y único grupo de captura del lado izquierdo de la alternancia (EXPRESSION) rest de la línea también se consumirá inmediatamente .* . Ahora mantenemos nuestro valor en el primer grupo de captura.

    • POSIX BRE

      Regex:

       \(\(\(EXPRESSION\).*\)*.\)* 

      Sed:

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

      Ejemplo (encontrar 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. Eso es todo. En cada motor de posición individual intenta hacer coincidir un dígito.

      enter image description here

      Si se encuentra, los otros dígitos siguientes se consumen y se capturan y el rest de la línea se empareja inmediatamente de lo contrario, ya que * significa más o cero , salta sobre el segundo grupo de captura \(\([0-9]\{1,\}\).*\)* y llega a un punto . para unir un solo personaje y este proceso continúa.

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

    Este enfoque coincidirá con la primera aparición de una cadena que está delimitada. Podemos llamarlo un 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 

    Primero, regex \(end\).* Coincide y captura el extremo del delimitador del primer end y sustituye todos los caracteres recientes capturados que es el delimitador final. En esta etapa, nuestra salida es: foobar start block #1 end .

    enter image description here

    Luego, el resultado 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 coincide con el rest de los caracteres.

    enter image description here


Respondiendo directamente a tu pregunta

Usando el enfoque n. ° 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/ 

Esto se puede hacer usando corte:

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

sed – juego no codicioso por Christoph Sieghart

El truco para obtener coincidencias no codiciosas en sed es hacer coincidir todos los caracteres, excluyendo el que termina el partido. Lo sé, una obviedad, pero perdí unos minutos preciosos y los guiones de shell deberían ser, después de todo, rápidos y fáciles. Entonces, en caso de que alguien más lo necesite:

Engaño codicioso

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

Juego no codicioso

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

Otra forma, sin usar regex, es usar el método de campo / delimitador, por ej.

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

sed ciertamente tiene su lugar, ¡pero este no es uno de ellos!

Como Dee ha señalado: solo usa 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 syntax 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 puede ver, este es un enfoque mucho más flexible.

(todo crédito a Dee)

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

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

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

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

 sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop" 
  • -r: Usa expresiones regulares extendidas (para + y paréntesis sin guardar)
  • “: loop”: define una nueva etiqueta llamada “loop”
  • -e: agrega comandos a sed
  • “t loop”: salta 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 colocarlo después de que termine el “bucle”, solo agregue este comando adicional al final del anterior línea de comando:

 -e "s,$,/," 

Como usted indicó específicamente que está tratando de usar sed (en lugar de perl, corte, 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 aquí .

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 \}

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

no te molestes, lo tengo en otro foro 🙂

sed 's|\(http:\/\/www\.[az.0-9]*\/\).*|\1| funciona también

Otra versión sed:

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

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

Aquí hay algo que puedes 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!

Esta es la forma robusta de hacer correspondencias no codiciosas de cadenas de caracteres múltiples usando sed. Digamos que quieres cambiar cada foo...bar a así que por ejemplo esta entrada:

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

debería convertirse en este resultado:

 ABC  GHI  NOP  TUV 

Para hacer eso, convierte foo y barra 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  GHI  NOP  TUV 

En lo de arriba:

  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 en.
  2. s/foo/{/g; s/bar/}/g s/foo/{/g; s/bar/}/g está convirtiendo foo y bar en { y } respectivamente
  3. s/{[^{}]*}/< &>/g está realizando el op que queremos – convirtiendo foo...bar en
  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 a sus caracteres originales.

Tenga en cuenta que lo anterior no se basa en ninguna cadena en particular que no esté presente en la entrada, ya que fabrica dichas cadenas en el primer paso, ni le importa qué ocurrencia de alguna expresión regular particular que quiera emparejar, 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 de seds, por ejemplo, para reemplazar solo la segunda ocurrencia:

 $ 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  NOP foo QRS bar TUV