web-dev-qa-db-fra.com

Correspondance regex non avide (réticent) dans sed?

J'essaie d'utiliser sed pour nettoyer les lignes d'URL afin d'extraire uniquement le domaine.

Donc de:

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

Je voudrais:

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

(avec ou sans entrainement, peu importe)

J'ai essayé:

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

et (échapper au quantificateur non gourmand)

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

mais je n'arrive pas à faire en sorte que le quantificateur non gourmand fonctionne, alors il finit toujours par faire correspondre la chaîne entière.

384
Joel

Les expressions rationnelles Posix/GNU de base ou étendues ne reconnaissent pas le quantificateur non gourmand; vous avez besoin d'un regex plus tard. Heureusement, les expressions rationnelles Perl dans ce contexte sont assez faciles à obtenir:

Perl -pe 's|(http://.*?/).*|\1|'
400
chaos

Dans ce cas spécifique, vous pouvez faire le travail sans utiliser une regex non gloutonne.

Essayez cette regex non gourmande [^/]* au lieu de .*?:

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

Avec sed, j’implémente généralement la recherche non gourmande en recherchant autre chose que le séparateur jusqu’au séparateur:

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

Sortie:

http://www.suon.co.uk

c'est:

  • ne pas sortir -n
  • rechercher, faire correspondre un motif, remplacer et imprimer s/<pattern>/<replace>/p
  • utilisez ; séparateur de commande de recherche au lieu de / pour faciliter la saisie de manière à ce que s;<pattern>;<replace>;p
  • mémoriser la correspondance entre crochets \( ... \), accessible ultérieurement avec \1, \2...
  • match http://
  • suivi de tout ce qui est entre parenthèses [], [ab/] signifierait soit a ou b ou /
  • premier ^ dans [] signifie not, suivi de tout sauf de la chose dans le []
  • alors [^/] signifie tout sauf / caractère
  • * consiste à répéter le groupe précédent pour que [^/]* signifie des caractères sauf /.
  • jusqu'ici sed -n 's;\(http://[^/]*\) signifie rechercher et mémoriser http://poursuivi par des caractères autres que / et mémoriser ce que vous avez trouvé.
  • nous voulons chercher jusqu'à la fin du domaine alors arrêtez-vous sur le prochain / ajoutez donc un autre / à la fin: sed -n 's;\(http://[^/]*\)/' mais nous voulons faire correspondre le reste de la ligne après le domaine, alors ajoutez .*
  • à présent, la correspondance mémorisée dans le groupe 1 (\1) est le domaine. remplacez la ligne correspondante par les éléments enregistrés dans le groupe \1 et imprimez: sed -n 's;\(http://[^/]*\)/.*;\1;p'

Si vous souhaitez également inclure une barre oblique inverse après le domaine, ajoutez une autre barre oblique inverse dans le groupe à retenir:

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

sortie:

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

sed ne supporte pas les opérateurs "non gourmands".

Vous devez utiliser l'opérateur "[]" pour exclure "/" de la correspondance.

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

P.S. il n'y a pas besoin de backslash "/".

36
andcoz

Simulation d'un quantificateur paresseux (peu gourmand) dans sed

Et toutes les autres saveurs de regex!

  1. Recherche de la première occurrence d'une expression:

    • POSIX ERE (en utilisant l'option -r)

      Regex:

      (EXPRESSION).*|.
      

      Sed:

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

      Exemple (recherche de la première séquence de chiffres) démonstration en direct:

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

      Comment ça marche ?

      Cette regex bénéficie d'une alternance |. À chaque position, le moteur recherche le premier côté de l’alternance (notre cible) et s’il ne correspond pas, le second côté de l’alternance comportant un point . correspond au caractère immédiat suivant.

      enter image description here

      Étant donné que l'indicateur global est défini, le moteur essaie de continuer à faire correspondre caractère par caractère jusqu'à la fin de la chaîne d'entrée ou de notre cible. Dès que le premier et le seul groupe de capture du côté gauche de l'alternance est mis en correspondance, (EXPRESSION) le reste de la ligne est consommé immédiatement ainsi .*. Nous conservons maintenant notre valeur dans le premier groupe de capture.

    • POSIX BRE

      Regex:

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

      Sed:

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

      Exemple (recherche de la première séquence de chiffres):

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

      Celui-ci est comme la version d'ERE mais sans alternance. C'est tout. À chaque position, le moteur essaie de faire correspondre un chiffre.

      enter image description here

      S'il est trouvé, les autres chiffres suivants sont consommés et capturés et le reste de la ligne est immédiatement mis en correspondance, car * signifie plus ou zéro , il saute le deuxième groupe de capture \(\([0-9]\{1,\}\).*\)* et arrive à un point . pour faire correspondre un seul caractère et ce processus se poursuit.

  2. Recherche de la première occurrence d'une expression délimitée :

    Cette approche correspond à la toute première occurrence d'une chaîne délimitée. Nous pouvons l'appeler un bloc de chaîne.

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

    Chaîne d'entrée:

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

    -EDE: end

    -SDE: start

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

    Sortie:

    start block #1 end
    

    Première regex \(end\).* associe et capture le délimiteur de première extrémité end et remplace tous les caractères correspondants par des caractères récemment capturés, le délimiteur de fin. A ce stade, notre sortie est: foobar start block #1 end.

    enter image description here

    Ensuite, le résultat est passé à la deuxième expression rationnelle \(\(start.*\)*.\)* qui est identique à la version POSIX BRE ci-dessus. Il correspond à un seul caractère si le délimiteur de début start ne correspond pas, sinon il correspond et capture le délimiteur de début et le reste des caractères.

    enter image description here


Répondre directement à votre question

En utilisant l’approche n ° 2 (expression délimitée), vous devez sélectionner deux expressions appropriées:

  • EDE: [^:/]\/

  • SDE: http:

Usage:

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

Sortie:

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

Solution non gourmande pour plus d'un personnage

Ce fil est vraiment vieux mais je suppose que les gens en ont toujours besoin. Disons que vous voulez tout tuer jusqu'à la toute première occurrence de HELLO. Vous ne pouvez pas dire [^HELLO]...

Donc, une solution de Nice implique deux étapes, en supposant que vous puissiez épargner un mot unique que vous n’attendez pas dans l’entrée, dites top_sekrit.

Dans ce cas, nous pouvons:

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

Bien sûr, avec une saisie plus simple, vous pouvez utiliser un mot plus petit, voire un seul caractère.

HTH!

21
ishahak

sed - correspondance non gourmande de Christoph Sieghart

L'astuce pour obtenir une correspondance non gourmande dans sed est de faire correspondre tous les caractères, à l'exception de celui qui termine la correspondance. Je le sais bien, mais j’ai perdu de précieuses minutes et les scripts Shell devraient être, après tout, rapides et faciles. Donc, au cas où quelqu'un d'autre en aurait besoin:

Appariement gourmand

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

Correspondance non gourmande

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

Cela peut être fait en utilisant cut:

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

une autre manière, sans utiliser regex, consiste à utiliser la méthode champs/délimiteur, par exemple

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

sed a certainement sa place mais ce n'est pas l'un d'entre eux!

Comme Dee l'a fait remarquer: utilisez simplement cut. C'est beaucoup plus simple et beaucoup plus sûr dans ce cas. Voici un exemple où nous extrayons divers composants de l'URL en utilisant la syntaxe 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-)

vous donne:

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

Comme vous pouvez le constater, cette approche est beaucoup plus flexible.

(tout crédit à Dee)

5
peterh

Il reste encore de l’espoir de résoudre ce problème en utilisant un sed (GNU) pur. Bien que cela ne soit pas une solution générique, dans certains cas, vous pouvez utiliser des "boucles" pour éliminer toutes les parties inutiles de la chaîne, comme ceci:

sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
  • -r: utilise une expression rationnelle étendue (pour les parenthèses + et les caractères d'échappement non échappés)
  • ": boucle": Définit une nouvelle étiquette nommée "boucle"
  • -e: ajoute des commandes à sed
  • "t loop": Revient à l'étiquette "loop" s'il y a eu substitution

Le seul problème ici est que le dernier caractère séparateur ('/') sera également coupé, mais si vous en avez vraiment besoin, vous pouvez le replacer simplement après la "boucle", ajoutez simplement cette commande supplémentaire à la fin de la précédente. ligne de commande:

-e "s,$,/,"
4
mTUX
sed 's|(http:\/\/[^\/]+\/).*|\1|'
3
Lucero

sed -E interprète les expressions régulières comme des expressions régulières étendues (modernes)

Mise à jour: -E sur MacOS X, -r dans GNU sed.

3
stepancheg

Parce que vous avez spécifiquement indiqué que vous essayez d'utiliser sed (au lieu de Perl, cut, etc.), essayez de regrouper. Ceci contourne l'identifiant non gourmand potentiellement non reconnu. Le premier groupe est le protocole (c'est-à-dire 'http: //', 'https: //', 'tcp: //', etc.). Le deuxième groupe est le domaine:

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

Si vous n'êtes pas familier avec le groupement, démarrez ici .

2
BrianB

Voici comment faire des correspondances non-gourmandes de chaînes multi-caractères à l'aide de sed. Disons que vous voulez changer chaque foo...bar en <foo...bar> ainsi, par exemple, cette entrée:

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

devrait devenir cette sortie:

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

Pour ce faire, convertissez foo et bar en caractères individuels, puis utilisez la négation de ces caractères entre eux:

$ 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

Au dessus:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/g est en train de convertir { et } en chaînes de caractères génériques ne pouvant pas exister dans l'entrée, ces caractères sont alors disponibles pour convertir foo et bar en.
  2. s/foo/{/g; s/bar/}/g convertit foo et bar en { et } respectivement
  3. s/{[^{}]*}/<&>/g exécute l'opération que nous souhaitons - convertir foo...bar en <foo...bar>
  4. s/}/bar/g; s/{/foo/g est en train de reconvertir { et } en foo et bar.
  5. s/@C/}/g; s/@B/{/g; s/@A/@/g est en train de reconvertir les chaînes d'espace réservé en leurs caractères d'origine.

Notez que ce qui précède ne repose pas sur le fait qu’une chaîne particulière n’est pas présente dans l’entrée car elle fabrique de telles chaînes dans la première étape, ni sur l’occurrence de toute expression rationnelle particulière que vous voulez faire correspondre puisque vous pouvez utiliser {[^{}]*} autant de fois que nécessaire dans l'expression pour isoler la correspondance réelle souhaitée et/ou avec l'opérateur de correspondance numérique seds, p.ex. pour ne remplacer que la 2ème occurrence:

$ 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
1
Ed Morton

Je me rends compte que c'est une vieille entrée, mais quelqu'un peut trouver cela utile. Comme le nom de domaine complet ne peut excéder une longueur totale de 253 caractères, remplacez. * Par.\{1, 255 \}

1
Iain Henderson

N'ayant pas encore vu cette réponse, voici comment procéder avec vi ou vim:

vi -c '%s/\(http:\/\/.\{-}\/\).*/\1/ge | wq' file &>/dev/null

Ceci exécute la substitution vi:%s globalement (le nom final g), évite de générer une erreur si le motif n'est pas trouvé (e), puis enregistre les modifications résultantes sur le disque et les abandonne. Le &>/dev/null empêche l’interface graphique de clignoter brièvement à l’écran, ce qui peut être gênant.

J'aime utiliser vi parfois pour des expressions rationnelles super compliquées, car (1) Perl est mort en train de mourir, (2) vim a un moteur regex avancé très, et (3) je connais déjà très bien les regex vi dans mes documents de modification quotidiens.

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

ne vous embêtez pas, je l'ai eu sur un autre forum :)

0
Dee

Une autre version de sed:

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

Il correspond à / suivi d'un caractère alphanumérique (donc pas d'une autre barre oblique) ainsi que du reste des caractères jusqu'à la fin de la ligne. Ensuite, il ne le remplace plus par rien (c.-à-d. Le supprime.)

0
sycamorex

sed 's|\(http:\/\/www\.[a-z.0-9]*\/\).*|\1| fonctionne aussi

0
GL2014

Voici quelque chose que vous pouvez faire avec une approche en deux étapes et awk:

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

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

J'espère que ça t'as aidé!

0
VINAY NAIR