Non gourmand (hésite) regex correspondant dans sed?

j'essaie d'utiliser sed pour nettoyer les lignes D'URLs pour extraire juste le domaine..

Donc à partir de:

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

je veux:

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

(avec ou sans le slash de trainling, ça n'a pas d'importance)

j'ai essayé:

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

et (s'échapper le non gourmand quantificateur)

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

mais je n'arrive pas à faire fonctionner le quantificateur non gourmand, donc il finit toujours par correspondre à la chaîne entière.

342
demandé sur Ondra Žižka 2009-07-09 14:47:31

20 réponses

ni Posix/GNU regex de base ni étendu ne reconnaît le quantificateur Non-cupide; vous avez besoin d'un regex plus tard. Heureusement, Perl regex pour ce contexte est assez facile à obtenir:

perl -pe 's|(http://.*?/).*||'
360
répondu chaos 2011-02-09 15:44:20

Try [^/]* au lieu de .*? :

sed 's|\(http://[^/]*/\).*||g'
202
répondu Gumbo 2015-05-08 04:00:18

avec sed, je mets habituellement en œuvre la recherche non-cupide en cherchant n'importe quoi sauf le séparateur jusqu'à ce que le séparateur:

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

sortie:

http://www.suon.co.uk

c'est:

  • n'a pas de sortie -n
  • de recherche, de correspondance de motif, de remplacer et d'impression s/<pattern>/<replace>/p
  • utiliser ; search command separator au lieu de / pour le rendre plus facile à taper ainsi s;<pattern>;<replace>;p
  • souvenez-vous du match entre parenthèses \( ... \) , plus tard accessible avec , ...
  • match http://
  • suivi de n'importe quoi entre parenthèses [] , [ab/] signifierait soit a ou b ou /
  • première ^ dans [] signifie not , a donc été suivi par rien, mais la chose dans la []
  • so [^/] signifie tout sauf / caractère
  • * doit être répété dans le groupe précédent de sorte que [^/]* signifie les caractères sauf / .
  • jusqu'à présent sed -n 's;\(http://[^/]*\) signifie rechercher et se souvenir http:// suivi de tous les caractères sauf / et se souvenir de ce que vous avez trouvé
  • nous voulons chercher jusqu'à la fin de domaine donc arrêter sur le prochain / donc ajouter un autre / à la fin: sed -n 's;\(http://[^/]*\)/' mais nous voulons correspondre au reste de la ligne après le domaine donc ajouter .*
  • maintenant la correspondance rappelée dans le groupe 1 ( ) est le domaine donc remplacer la ligne appariée avec des trucs enregistrés dans le groupe et imprimer: sed -n 's;\(http://[^/]*\)/.*;;p'

si vous voulez inclure backslash après le domaine aussi bien, alors ajouter un backslash de plus dans le groupe à retenir:

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

sortie:

http://www.suon.co.uk/
100
répondu stefanB 2014-04-10 09:54:15

sed ne supporte pas l'opérateur" non greedy".

Vous devez utiliser des "[]" opérateur d'exclure les "/" de match.

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

P. S. Il n'y a pas besoin de revenir en arrière "/".

34
répondu andcoz 2009-07-09 11:08:44

Simulant paresseux (onu-gourmand) quantificateur dans sed

et toutes les autres saveurs regex!

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

    • POSIX ERE (en utilisant -r option)

      Regex:

      (EXPRESSION).*|.
      

      Sed:

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

      exemple (trouver la première séquence de chiffres) live demo :

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

      comment ça marche ?

      ce regex bénéficie d'une alternance | . À chaque position le moteur cherchera le premier côté de l'alternance (notre cible) et s'il n'est pas assorti au deuxième côté de l'alternance qui a un point . correspond au prochain caractère immédiat.

      enter image description here

      depuis que le drapeau global est activé, engine essaye de continuer à faire correspondre les caractères jusqu'à la fin de la chaîne de saisie ou de notre cible. Dès que le premier et seul groupe de capture du côté gauche de l'alternance est apparié (EXPRESSION) reste de la ligne est consommé immédiatement ainsi .* . Nous tenons maintenant notre valeur dans le premier capture d'un groupe.

    • POSIX BRE

      Regex:

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

      Sed:

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

      exemple (trouver la première séquence de chiffres):

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

      celui-ci est comme version ERE mais sans alternance. C'est tout. A 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 saisis et le reste de la ligne est apparié immédiatement sinon car * signifie plus ou zéro il saute sur le deuxième groupe de capture \(\([0-9]\{1,\}\).*\)* et arrive à un point . pour correspondre à un seul caractère et ce processus continue.

  2. constatation de la première occurrence d'un délimité expression:

    cette approche correspondra à la toute première occurrence d'une chaîne délimitée. On peut l'appeler un bloc de ficelle.

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

    chaîne de saisie:

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

    - EDE: end

    - SDE: start

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

    sortie:

    start block #1 end
    

    First regex \(end\).* correspond et capture le premier délimiteur d'extrémité end et substitue tous correspondent avec des caractères capturés récemment qui c'est la fin de délimiteur. À ce stade, notre production est: foobar start block #1 end .

    enter image description here

    puis le résultat est passé au second regex \(\(start.*\)*.\)* qui est le même as POSIX BRE version above. Il correspond à un seul caractère si le délimiteur de départ start n'est pas assorti autrement il correspond et capture le délimiteur de départ et correspond au reste des caractères.

    enter image description here


répondre directement à votre question

en utilisant l'approche #2 (délimitée expression) vous devez sélectionner deux expressions appropriées:

  • EDE: [^:/]\/

  • SDE: http:

Utilisation:

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

sortie:

http://www.suepearson.co.uk/
20
répondu revo 2016-09-28 21:36:35

Non-greedy solution pour plus d'un seul caractère

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

donc une bonne solution implique deux étapes, en supposant que vous pouvez é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 entrée plus simple, vous pouvez utiliser un mot plus petit, ou peut-être même un seul caractère.

HTH!

19
répondu ishahak 2018-06-15 00:52:10

cela peut être fait en utilisant la Coupe:

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
16
répondu Dee 2011-08-15 04:04:48

sed non gourmande de correspondance par Christoph Sieghart

le truc pour obtenir non greedy matching dans sed est de faire correspondre tous les caractères à l'exclusion de celui qui termine le match. Je sais, c'est facile, mais j'ai perdu de précieuses minutes et les scripts shell devraient être rapides et faciles. Donc, au cas où quelqu'un d'autre pourrait en avoir besoin:

Gourmand matching

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

Non gourmande de correspondance

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
11
répondu gresolio 2017-10-12 21:45:39

une autre façon, n'utilisant pas regex, est d'utiliser la méthode fields/delimiter eg

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print ,,}' OFS="/"
9
répondu ghostdog74 2009-07-09 10:59:12

sed a certainement sa place, mais pas celle-ci !

comme Dee L'a souligné: il suffit d'utiliser cut . Il est beaucoup plus simple et beaucoup plus sûre dans ce cas. Voici un exemple où nous extrayons divers composants de L'URL en utilisant la syntaxe 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-)

vous donne:

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

Comme vous pouvez le voir c'est beaucoup plus souples.

(tout le crédit à Dee)

5
répondu peterh 2013-08-30 14:41:15
sed 's|(http:\/\/[^\/]+\/).*||'
3
répondu Lucero 2009-07-09 10:58:59

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
répondu stepancheg 2011-03-07 17:28:00

il y a encore de l'espoir de résoudre ce problème en utilisant pur (GNU) sed. Malgré cela n'est 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://.+)/.*||' -e "t loop"
  • - r: utiliser regex étendu (pour + et sans parenthèses)
  • ": loop": définissez une nouvelle étiquette appelée "loop"
  • - e: ajouter des commandes à sed
  • " t loop": revenir à l'étiquette "loop"" s'il y avait une substitution réussie

le seul problème ici est qu'il va également couper le dernier caractère de séparateur ( ' / '), mais si vous en avez vraiment besoin, vous pouvez tout simplement le remettre après la "boucle" terminée, il suffit d'ajouter cette commande supplémentaire à la fin de la ligne de commande précédente:

-e "s,$,/,"
3
répondu mTUX 2016-08-01 12:52:19

parce que vous avez spécifiquement déclaré que vous essayez d'utiliser sed (au lieu de perl, cut, etc.), essayez de regrouper. Ceci contourne l'identifiant non-cupide 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|^\(.*//\)\([^/]*\).*$||"

si vous n'êtes pas familier avec le regroupement, commencez ici .

2
répondu BrianB 2017-05-23 12:26:32

je sais que c'est une vieille entrée, mais quelqu'un pourrait la trouver utile. Comme le nom de domaine complet ne peut pas dépasser une longueur totale de 253 caractères remplacer .* avec. \{1, 255\}

1
répondu Iain Henderson 2011-06-29 15:49:11

c'est la façon de faire l'appariement Non-cupide de chaînes à plusieurs caractères en utilisant sed. Disons que vous voulez changer chaque foo...bar en <foo...bar> donc 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 faire que vous convertissez foo et bar en caractères individuels et puis utiliser 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

dans le texte ci-dessus:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/g convertit { et } en chaînes de caractères qui ne peuvent pas exister dans l'entrée de sorte que ces caractères sont alors disponibles pour convertir foo et bar en.
  2. s/foo/{/g; s/bar/}/g est en train de convertir foo et bar en { et } respectivement
  3. s/{[^{}]*}/<&>/g exécute l'opération que nous voulons-conversion foo...bar en <foo...bar>
  4. s/}/bar/g; s/{/foo/g convertit { et } en foo et bar .
  5. s/@C/}/g; s/@B/{/g; s/@A/@/g est en train de convertir les chaînes de caractères placeholder en leurs caractères originaux.

notez que ce qui précède ne repose pas sur une chaîne particulière n'étant pas présent dans l'entrée car il fabrique de telles chaînes dans la première étape, il ne se soucie pas non plus quelle occurrence d'un regexp particulier vous voulez faire correspondre depuis vous pouvez utiliser {[^{}]*} autant de fois que nécessaire dans l'expression pour isoler la correspondance réelle que vous voulez et/ou avec l'opérateur de correspondance numérique seds, par exemple pour remplacer seulement 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
répondu Ed Morton 2018-06-27 02:21:37
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*||'

ne dérange pas, je l'ai eu sur un autre forum :)

0
répondu Dee 2011-06-30 02:15:19

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

0
répondu GL2014 2013-08-01 20:39:47

une autre version de sed:

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

correspond à / suivi d'un caractère alphanumérique (donc pas une autre barre oblique vers l'avant) ainsi que le reste des caractères jusqu'à la fin de la ligne. Par la suite, il le remplace par rien (c.-à-d. supprime.)

0
répondu sycamorex 2016-02-02 00:27:37

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,"151900920") ;  
  sub(/\|\|.*/,"",var);  
  print var  
}'  

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

Espère que ça aide!

0
répondu VINAY NAIR 2017-06-09 05:26:19