Je souhaiterais écrire une sorte fonction de fonction «
grep_debut_verbatim » qui vérifie les conditions suivantes :
* elle lit son entrée standard
* elle attend un unique argument (une chaîne de caractères)
* elle renvoie 0 si la chaîne donnée en argument se trouve en début de
ligne d'au moins une ligne de l'entrée standard et renvoie autre chose
que 0 sinon.
* la chaîne donnée en argument ne doit pas être vue comme une regex mais
comme une « chaîne brute », ie les caractères habituellement spéciaux
pour une regex ne doivent pas l'être et doivent représenter eux-même.
En cherchant un peu, j'ai fait ça :
grep_debut_verbatim ()
{
local motif
# On échappe tous les caractères du motif pour
# éviter qu'un caractère spécial soit interprété
# dans le grep ci-dessous.
motif=$(echo "$1" | sed 's/./\\&/g')
while read; do
if echo "$REPLY" | grep -q "^$motif"; then
return 0
fi
done
return 1
}
Est-ce que ça vous semble correct ? Je marche un peu sur des œufs avec
cette fonction au niveau de la lecture de l'entrée standard (j'utilise
une variable « made in bash » mais il y a peut-être plus propre) et au
niveau de l'échappement de tous les caractères du motif où ça semble un
peu compliqué mais je n'ai pas trouvé d'autre moyen que celui-là.
Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
Francois Lafont
Le 06/11/2012 15:41, Francois Lafont a écrit :
grep_debut_verbatim () { local motif # On échappe tous les caractères du motif pour # éviter qu'un caractère spécial soit interprété # dans le grep ci-dessous. motif=$(echo "$1" | sed 's/./&/g')
while read; do
Rectification : c'est « while read -r; do »
if echo "$REPLY" | grep -q "^$motif"; then return 0 fi done return 1 }
-- François Lafont
Le 06/11/2012 15:41, Francois Lafont a écrit :
grep_debut_verbatim ()
{
local motif
# On échappe tous les caractères du motif pour
# éviter qu'un caractère spécial soit interprété
# dans le grep ci-dessous.
motif=$(echo "$1" | sed 's/./\&/g')
while read; do
Rectification : c'est « while read -r; do »
if echo "$REPLY" | grep -q "^$motif"; then
return 0
fi
done
return 1
}
grep_debut_verbatim () { local motif # On échappe tous les caractères du motif pour # éviter qu'un caractère spécial soit interprété # dans le grep ci-dessous. motif=$(echo "$1" | sed 's/./&/g')
while read; do
Rectification : c'est « while read -r; do »
if echo "$REPLY" | grep -q "^$motif"; then return 0 fi done return 1 }
-- François Lafont
Olivier Miakinen
Bonjour,
Le 06/11/2012 15:41, Francois Lafont a écrit :
* elle lit son entrée standard * elle attend un unique argument (une chaîne de caractères) * elle renvoie 0 si la chaîne donnée en argument se trouve en début de ligne d'au moins une ligne de l'entrée standard et renvoie autre chose que 0 sinon. * la chaîne donnée en argument ne doit pas être vue comme une regex mais comme une « chaîne brute », ie les caractères habituellement spéciaux pour une regex ne doivent pas l'être et doivent représenter eux-même.
[...]
motif=$(echo "$1" | sed 's/./&/g') [...] grep -q "^$motif"
Mon premier test a échoué, dans cygwin sur Windows : j'ai mis un « { » dans la chaîne à tester, qui est devenu spécial avec « { ». Ça s'est mis à marcher en remplaçant « grep » par « grep -E », mais le 'man' n'est pas très rassurant sur ce point :
<cit.> La version traditionnelle d'egrep ne connaît pas le méta-caractère {, et certaines implantations d'egrep utilisent { à la place, si bien que des scripts shell portables devraient éviter { dans les motifs d'egrep et utiliser [{] pour désigner un caractère {. </cit.>
En fait, il me semblerait plus simple de ne pas utiliser grep du tout pour comparer deux chaînes, mais plutôt de tronquer les lignes de l'entrée standard à la longueur voulue avant des les comparer. La longueur en question, en bash c'est par exemple ${#1} (ou ${#motif} si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon portable.
* elle lit son entrée standard
* elle attend un unique argument (une chaîne de caractères)
* elle renvoie 0 si la chaîne donnée en argument se trouve en début de
ligne d'au moins une ligne de l'entrée standard et renvoie autre chose
que 0 sinon.
* la chaîne donnée en argument ne doit pas être vue comme une regex mais
comme une « chaîne brute », ie les caractères habituellement spéciaux
pour une regex ne doivent pas l'être et doivent représenter eux-même.
[...]
motif=$(echo "$1" | sed 's/./\&/g')
[...] grep -q "^$motif"
Mon premier test a échoué, dans cygwin sur Windows : j'ai mis un « { »
dans la chaîne à tester, qui est devenu spécial avec « { ». Ça s'est
mis à marcher en remplaçant « grep » par « grep -E », mais le 'man'
n'est pas très rassurant sur ce point :
<cit.>
La version traditionnelle d'egrep ne connaît pas le méta-caractère {, et
certaines implantations d'egrep utilisent { à la place, si bien que des
scripts shell portables devraient éviter { dans les motifs d'egrep et
utiliser [{] pour désigner un caractère {.
</cit.>
En fait, il me semblerait plus simple de ne pas utiliser grep du tout
pour comparer deux chaînes, mais plutôt de tronquer les lignes de
l'entrée standard à la longueur voulue avant des les comparer. La
longueur en question, en bash c'est par exemple ${#1} (ou ${#motif}
si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon
portable.
* elle lit son entrée standard * elle attend un unique argument (une chaîne de caractères) * elle renvoie 0 si la chaîne donnée en argument se trouve en début de ligne d'au moins une ligne de l'entrée standard et renvoie autre chose que 0 sinon. * la chaîne donnée en argument ne doit pas être vue comme une regex mais comme une « chaîne brute », ie les caractères habituellement spéciaux pour une regex ne doivent pas l'être et doivent représenter eux-même.
[...]
motif=$(echo "$1" | sed 's/./&/g') [...] grep -q "^$motif"
Mon premier test a échoué, dans cygwin sur Windows : j'ai mis un « { » dans la chaîne à tester, qui est devenu spécial avec « { ». Ça s'est mis à marcher en remplaçant « grep » par « grep -E », mais le 'man' n'est pas très rassurant sur ce point :
<cit.> La version traditionnelle d'egrep ne connaît pas le méta-caractère {, et certaines implantations d'egrep utilisent { à la place, si bien que des scripts shell portables devraient éviter { dans les motifs d'egrep et utiliser [{] pour désigner un caractère {. </cit.>
En fait, il me semblerait plus simple de ne pas utiliser grep du tout pour comparer deux chaînes, mais plutôt de tronquer les lignes de l'entrée standard à la longueur voulue avant des les comparer. La longueur en question, en bash c'est par exemple ${#1} (ou ${#motif} si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon portable.
grep_debut_verbatim () { local motif # On échappe tous les caractères du motif pour # éviter qu'un caractère spécial soit interprété # dans le grep ci-dessous. motif=$(echo "$1" | sed 's/./&/g')
while read; do
Rectification : c'est « while read -r; do »
if echo "$REPLY" | grep -q "^$motif"; then return 0 fi done return 1 }
Même avec cet ajustement, la fonction ne marche pas tout à fait. Par exemple, je me fais « bananer » dans ce cas tout bête :
$ echo 'w' | grep_debut_verbatim 'w'; echo $? 1
car w possède une signification au niveau des regex dans grep.
Je précise que j'ai bien vu l'option -F de grep qui semble faire exactement ce que je veux (ie le motif se représente lui-même et basta) mais malheureusement je perds l'interprétation du caractère ^ dont j'ai besoin car je veux que la correspondance se fasse au niveau d'un début de ligne.
Voilà où j'en suis.
-- François Lafont
Re,
Le 06/11/2012 16:07, Francois Lafont a écrit :
Le 06/11/2012 15:41, Francois Lafont a écrit :
grep_debut_verbatim ()
{
local motif
# On échappe tous les caractères du motif pour
# éviter qu'un caractère spécial soit interprété
# dans le grep ci-dessous.
motif=$(echo "$1" | sed 's/./\&/g')
while read; do
Rectification : c'est « while read -r; do »
if echo "$REPLY" | grep -q "^$motif"; then
return 0
fi
done
return 1
}
Même avec cet ajustement, la fonction ne marche pas tout à fait. Par
exemple, je me fais « bananer » dans ce cas tout bête :
$ echo 'w' | grep_debut_verbatim 'w'; echo $?
1
car w possède une signification au niveau des regex dans grep.
Je précise que j'ai bien vu l'option -F de grep qui semble faire
exactement ce que je veux (ie le motif se représente lui-même et basta)
mais malheureusement je perds l'interprétation du caractère ^ dont j'ai
besoin car je veux que la correspondance se fasse au niveau d'un début
de ligne.
grep_debut_verbatim () { local motif # On échappe tous les caractères du motif pour # éviter qu'un caractère spécial soit interprété # dans le grep ci-dessous. motif=$(echo "$1" | sed 's/./&/g')
while read; do
Rectification : c'est « while read -r; do »
if echo "$REPLY" | grep -q "^$motif"; then return 0 fi done return 1 }
Même avec cet ajustement, la fonction ne marche pas tout à fait. Par exemple, je me fais « bananer » dans ce cas tout bête :
$ echo 'w' | grep_debut_verbatim 'w'; echo $? 1
car w possède une signification au niveau des regex dans grep.
Je précise que j'ai bien vu l'option -F de grep qui semble faire exactement ce que je veux (ie le motif se représente lui-même et basta) mais malheureusement je perds l'interprétation du caractère ^ dont j'ai besoin car je veux que la correspondance se fasse au niveau d'un début de ligne.
Voilà où j'en suis.
-- François Lafont
Olivier Miakinen
Le 06/11/2012 16:27, Francois Lafont a écrit :
[...] je me fais « bananer » dans ce cas tout bête :
$ echo 'w' | grep_debut_verbatim 'w'; echo $? 1
car w possède une signification au niveau des regex dans grep.
Eh oui. La solution compliquée consisterait à modifier ton sed pour qu'il transforme un caractère c en c si ce n'est ni une lettre ni une accolade, en [c] si c'est une accolade, et en c si c'est une lettre.
Mais la solution simple est vraiment de ne pas utiliser grep du tout.
Le 06/11/2012 16:27, Francois Lafont a écrit :
[...] je me fais « bananer » dans ce cas tout bête :
$ echo 'w' | grep_debut_verbatim 'w'; echo $?
1
car w possède une signification au niveau des regex dans grep.
Eh oui. La solution compliquée consisterait à modifier ton sed
pour qu'il transforme un caractère c en c si ce n'est ni une
lettre ni une accolade, en [c] si c'est une accolade, et en c
si c'est une lettre.
Mais la solution simple est vraiment de ne pas utiliser grep
du tout.
[...] je me fais « bananer » dans ce cas tout bête :
$ echo 'w' | grep_debut_verbatim 'w'; echo $? 1
car w possède une signification au niveau des regex dans grep.
Eh oui. La solution compliquée consisterait à modifier ton sed pour qu'il transforme un caractère c en c si ce n'est ni une lettre ni une accolade, en [c] si c'est une accolade, et en c si c'est une lettre.
Mais la solution simple est vraiment de ne pas utiliser grep du tout.
Francois Lafont
Le 06/11/2012 16:23, Olivier Miakinen a écrit :
En fait, il me semblerait plus simple de ne pas utiliser grep du tout pour comparer deux chaînes, mais plutôt de tronquer les lignes de l'entrée standard à la longueur voulue avant des les comparer.
Et oui. Merci pour cette idée simple et pleine de bon sens je pense. En effet, je sentais bien que je partais dans du n'importe quoi mon grep et mon sed pour échapper tous les caractères.
La longueur en question, en bash c'est par exemple ${#1} (ou ${#motif} si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon portable.
Ok. Merci pour les explications. Ceci étant, avec cut, il me semble que je peux avoir des soucis avec des caractères non ASCII. Exemple :
$ echo "ÀÉÇé" | cut -b -3 À
Ceci étant je pense que ton principe de départ est le bon. Je vais creuser la question.
Merci pour ton aide.
-- François Lafont
Le 06/11/2012 16:23, Olivier Miakinen a écrit :
En fait, il me semblerait plus simple de ne pas utiliser grep du tout
pour comparer deux chaînes, mais plutôt de tronquer les lignes de
l'entrée standard à la longueur voulue avant des les comparer.
Et oui. Merci pour cette idée simple et pleine de bon sens je pense. En
effet, je sentais bien que je partais dans du n'importe quoi mon grep et
mon sed pour échapper tous les caractères.
La
longueur en question, en bash c'est par exemple ${#1} (ou ${#motif}
si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon
portable.
En fait, il me semblerait plus simple de ne pas utiliser grep du tout pour comparer deux chaînes, mais plutôt de tronquer les lignes de l'entrée standard à la longueur voulue avant des les comparer.
Et oui. Merci pour cette idée simple et pleine de bon sens je pense. En effet, je sentais bien que je partais dans du n'importe quoi mon grep et mon sed pour échapper tous les caractères.
La longueur en question, en bash c'est par exemple ${#1} (ou ${#motif} si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon portable.
Ok. Merci pour les explications. Ceci étant, avec cut, il me semble que je peux avoir des soucis avec des caractères non ASCII. Exemple :
$ echo "ÀÉÇé" | cut -b -3 À
Ceci étant je pense que ton principe de départ est le bon. Je vais creuser la question.
Merci pour ton aide.
-- François Lafont
Olivier Miakinen
Le 06/11/2012 16:23, Olivier Miakinen a écrit :
En fait, il me semblerait plus simple de ne pas utiliser grep du tout pour comparer deux chaînes, mais plutôt de tronquer les lignes de l'entrée standard à la longueur voulue avant des les comparer. La longueur en question, en bash c'est par exemple ${#1} (ou ${#motif} si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon portable.
Bon, j'ai trouvé un moyen qui est probablement portable, mais au prix de la lisibilité car c'est vraiment affreux.
$ chaine="ABCDE" $ motif=$(echo $chaine | sed 's/././g') $ echo "123456789" | sed "s/($motif).*/1/" 12345 $ echo "ABCDEFGHIJKLMNOPQRSTU" | sed "s/($motif).*/1/" ABCDE
Le 06/11/2012 16:23, Olivier Miakinen a écrit :
En fait, il me semblerait plus simple de ne pas utiliser grep du tout
pour comparer deux chaînes, mais plutôt de tronquer les lignes de
l'entrée standard à la longueur voulue avant des les comparer. La
longueur en question, en bash c'est par exemple ${#1} (ou ${#motif}
si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon
portable.
Bon, j'ai trouvé un moyen qui est probablement portable, mais au prix
de la lisibilité car c'est vraiment affreux.
$ chaine="ABCDE"
$ motif=$(echo $chaine | sed 's/././g')
$ echo "123456789" | sed "s/($motif).*/1/"
12345
$ echo "ABCDEFGHIJKLMNOPQRSTU" | sed "s/($motif).*/1/"
ABCDE
En fait, il me semblerait plus simple de ne pas utiliser grep du tout pour comparer deux chaînes, mais plutôt de tronquer les lignes de l'entrée standard à la longueur voulue avant des les comparer. La longueur en question, en bash c'est par exemple ${#1} (ou ${#motif} si tu as fait motif=$1 ). Je ne sais pas comment le faire de façon portable.
Bon, j'ai trouvé un moyen qui est probablement portable, mais au prix de la lisibilité car c'est vraiment affreux.
$ chaine="ABCDE" $ motif=$(echo $chaine | sed 's/././g') $ echo "123456789" | sed "s/($motif).*/1/" 12345 $ echo "ABCDEFGHIJKLMNOPQRSTU" | sed "s/($motif).*/1/" ABCDE
Olivier Miakinen
Le 06/11/2012 16:43, Francois Lafont a écrit :
Ok. Merci pour les explications. Ceci étant, avec cut, il me semble que je peux avoir des soucis avec des caractères non ASCII. Exemple :