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.
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://.*?/).*||'
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 ainsis;<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 soita
oub
ou/
- première
^
dans[]
signifienot
, 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 souvenirhttp://
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/
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 "/".
Simulant paresseux (onu-gourmand) quantificateur dans sed
et toutes les autres saveurs regex!
-
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.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.
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.
-
-
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
.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épartstart
n'est pas assorti autrement il correspond et capture le délimiteur de départ et correspond au reste des caractères.
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/
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!
cela peut être fait en utilisant la Coupe:
echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
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
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="/"
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)
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.
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,$,/,"
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 .
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\}
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:
-
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 convertirfoo
etbar
en. -
s/foo/{/g; s/bar/}/g
est en train de convertirfoo
etbar
en{
et}
respectivement -
s/{[^{}]*}/<&>/g
exécute l'opération que nous voulons-conversionfoo...bar
en<foo...bar>
-
s/}/bar/g; s/{/foo/g
convertit{
et}
enfoo
etbar
. -
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
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*||'
ne dérange pas, je l'ai eu sur un autre forum :)
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.)
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!