Équivalent d'une commande mais avec awk au lieu de sed

12 réponses
Avatar
Francois Lafont
Bonjour à tous,

Je ne suis vraiment pas un expert en sed et en awk. Quand il faut faire
une petite substitution dans un fichier j'utilise sed et quand c'est
plus compliqué je passe à awk (non sans l'aide de Google ou autre).


1. Pouvez-vous me donner l'équivalent de la commande ci-dessous mais en
utilisant awk au lieu de sed ?

sed -r \
-e 's/^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$'/\1/g' \
fichier

La regex choisie est un exemple, ça pourrait être autre chose. En
cherchant dans la page man de awk, j'ai trouvé comme équivalent ceci :

awk '{
c=gensub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "\\1", "g")
print c
}' fichier

a) Est-ce que cela vous paraît être un équivalent correct ?
b) N'y a-t-il pas plus simple quand même ?


2) Dans la commande juste au dessus (celle avec awk), j'ai voulu
remplacer la fonction gensub() par gsub() mais en vain. Par exemple,
ceci :

awk '{
gsub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "\\1")
print c
}' fichier

m'affiche « \1 » au lieu de m'afficher le premier sous-groupe dans ma
regex (comme c'est le avec gensub). Pourquoi ?


3) Enfin, connaissez-vous un équivalent avec awk de la commande de la
question 1 à laquelle j'ajoute l'option -i (bien pratique je trouve) ?
Autrement dit un équivalent avec awk de ça :

sed -i -r -e 's/regex/remplacement/g' fichier

Je ne vois pas d'autre moyen que de faire ça :

awk '{ c=gensub("regex", "remplacement", "g"); print c }' \
fichier > /tmp/fichier_temp
cat fichier_temp > fichier
rm /tmp/fichier_temp

ce qui est tout de même beaucoup plus bavard.

Merci pour votre aide.

--
François Lafont

10 réponses

1 2
Avatar
Benoit Izac
Bonjour,

le 01/09/2012 à 14:30, Francois Lafont a écrit dans le message
<5041ffe1$0$6111$ :

Je ne suis vraiment pas un expert en sed et en awk. Quand il faut faire
une petite substitution dans un fichier j'utilise sed et quand c'est
plus compliqué je passe à awk (non sans l'aide de Google ou autre).



[tu devrais poser ce genre de question dans fr.comp.os.unix]

1. Pouvez-vous me donner l'équivalent de la commande ci-dessous mais en
utilisant awk au lieu de sed ?

sed -r
-e 's/^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$'/1/g'


^
mauvais copier/coller !
fichier

La regex choisie est un exemple, ça pourrait être autre chose. En
cherchant dans la page man de awk, j'ai trouvé comme équivalent ceci :

awk '{
c=gensub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "1", "g")
print c
}' fichier

a) Est-ce que cela vous paraît être un équivalent correct ?



gensub n'est pas standard et est propre à gawk.
<http://pubs.opengroup.org/onlinepubs/009695399/utilities/awk.html>

b) N'y a-t-il pas plus simple quand même ?



Tout dépend de ce que tu veux faire ; une autre méthode :
awk -F '[ t]*=[ t]*' '{sub(/[ t]*$/,"");print $2}' fichier

2) Dans la commande juste au dessus (celle avec awk), j'ai voulu
remplacer la fonction gensub() par gsub() mais en vain. Par exemple,
ceci :

awk '{
gsub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "1")
print c
}' fichier

m'affiche « 1 » au lieu de m'afficher le premier sous-groupe dans ma
regex (comme c'est le avec gensub). Pourquoi ?



Parce que awk (sans extension), ne supporte pas les backreferences.

3) Enfin, connaissez-vous un équivalent avec awk de la commande de la
question 1 à laquelle j'ajoute l'option -i (bien pratique je trouve) ?



Non. Si tu veux vraiment pouvoir faire tout cela (et bien plus), je ne
peux que te conseiller de te tourner vers un langage de programmation
comme Perl.

awk '{ c=gensub("regex", "remplacement", "g"); print c }'
fichier > /tmp/fichier_temp
cat fichier_temp > fichier
rm /tmp/fichier_temp



Si les permissions sont standards, on peut faire :
mv /tmp/fichier_temp fichier

--
Benoit Izac
Avatar
Benoit Izac
Dans le message , le 01/09/2012 à 22:38, j'ai
écrit :

3) Enfin, connaissez-vous un équivalent avec awk de la commande de la
question 1 à laquelle j'ajoute l'option -i (bien pratique je trouve) ?



Non. Si tu veux vraiment pouvoir faire tout cela (et bien plus), je ne
peux que te conseiller de te tourner vers un langage de programmation
comme Perl.



Pour l'exemple :
perl -pe 's/^vars*=s*(.*?)s*$/$1n/' -i fichier

--
Benoit Izac
Avatar
moi-meme
Le Sat, 01 Sep 2012 14:30:25 +0200, Francois Lafont a écrit :

Je ne suis vraiment pas un expert en sed et en awk. Quand il faut faire
une petite substitution dans un fichier j'utilise sed et quand c'est
plus compliqué je passe à awk (non sans l'aide de Google ou autre).



je n'utilise qu'un peu awk.

Si tu sais ce que tu veux essaie txt2regex un script python.

http://sourceforge.net/projects/txt2regex/
Avatar
Francois Lafont
Le 02/09/2012 17:55, moi-meme a écrit :

Je ne suis vraiment pas un expert en sed et en awk. Quand il faut faire
une petite substitution dans un fichier j'utilise sed et quand c'est
plus compliqué je passe à awk (non sans l'aide de Google ou autre).



je n'utilise qu'un peu awk.

Si tu sais ce que tu veux essaie txt2regex un script python.

http://sourceforge.net/projects/txt2regex/



Merci pour ce lien mais ce n'est pas vraiment l'aspect regex qui me pose
souci. Je connais suffisamment bien les regex par rapport à l'usage que
j'en ai.

Mon souci est vraiment d'arriver trouver avec awk un équivalent d'un
truc assez basique que je fais avec sed. Ce qui m'étonne c'est que
l'équivalent que j'ai trouvé avec awk me semble bien compliqué par
rapport à la simplicité de la tâche. Alors je me dis que je passe
peut-être à côté de quelque chose.

--
François Lafont
Avatar
moi-meme
Le Mon, 03 Sep 2012 00:49:07 +0200, Francois Lafont a écrit :

Mon souci est vraiment d'arriver trouver avec awk un équivalent d'un
truc assez basique que je fais avec sed. Ce qui m'étonne c'est que
l'équivalent que j'ai trouvé avec awk me semble bien compliqué par
rapport à la simplicité de la tâche. Alors je me dis que je passe
peut-être à côté de quelque chose.



Ils ne sont pas équivalents.

Sans vouloir troller, awk m'a l'air plus puissant en algorithmique et sed
meilleur en substitution de chaînes.
Avatar
Marc Boyer
Le 01-09-2012, Francois Lafont a écrit :
1. Pouvez-vous me donner l'équivalent de la commande ci-dessous mais en
utilisant awk au lieu de sed ?

sed -r
-e 's/^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$'/1/g'
fichier

La regex choisie est un exemple, ça pourrait être autre chose. En
cherchant dans la page man de awk, j'ai trouvé comme équivalent ceci :

awk '{
c=gensub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "1", "g")
print c
}' fichier

a) Est-ce que cela vous paraît être un équivalent correct ?



Oui

b) N'y a-t-il pas plus simple quand même ?



Non. Pas à ma connaissance.

2) Dans la commande juste au dessus (celle avec awk), j'ai voulu
remplacer la fonction gensub() par gsub() mais en vain. Par exemple,
ceci :

awk '{
gsub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "1")
print c
}' fichier

m'affiche « 1 » au lieu de m'afficher le premier sous-groupe dans ma
regex (comme c'est le avec gensub). Pourquoi ?



Parce que c'est la spec de gsub, et que gensub a été visiblement écrit
pour pouvoir faire des correspondances de sous-chaines.

3) Enfin, connaissez-vous un équivalent avec awk de la commande de la
question 1 à laquelle j'ajoute l'option -i (bien pratique je trouve) ?



L'option -i est (de tête) une extention du GNU sed.
Elle n'existe visiblement pas dans le GNU awk.

awk '{ c=gensub("regex", "remplacement", "g"); print c }'
fichier > /tmp/fichier_temp
cat fichier_temp > fichier
rm /tmp/fichier_temp

ce qui est tout de même beaucoup plus bavard.



Et encore, tu n'as fait aucun test d'échec.

Marc Boyer
--
À mesure que les inégalités regressent, les attentes se renforcent.
François Dubet
Avatar
Marc Boyer
Le 02-09-2012, Francois Lafont a écrit :

Mon souci est vraiment d'arriver trouver avec awk un équivalent d'un
truc assez basique que je fais avec sed. Ce qui m'étonne c'est que
l'équivalent que j'ai trouvé avec awk me semble bien compliqué par
rapport à la simplicité de la tâche. Alors je me dis que je passe
peut-être à côté de quelque chose.



Je ne vois pas en quoi c'est "bien compliqué".
Tu fais un copier/coller de ta regex dans gensub,
puis un print.

Awk a un paradigme different de sed. Il ne fait pas
par défaut de print de l'espace de travail, donc, il te faut
le faire.

Marc Boyer
--
À mesure que les inégalités regressent, les attentes se renforcent.
François Dubet
Avatar
Marc Boyer
Le 03-09-2012, moi-meme a écrit :
Le Mon, 03 Sep 2012 00:49:07 +0200, Francois Lafont a écrit :

Mon souci est vraiment d'arriver trouver avec awk un équivalent d'un
truc assez basique que je fais avec sed. Ce qui m'étonne c'est que
l'équivalent que j'ai trouvé avec awk me semble bien compliqué par
rapport à la simplicité de la tâche. Alors je me dis que je passe
peut-être à côté de quelque chose.



Sans vouloir troller, awk m'a l'air plus puissant en algorithmique et sed
meilleur en substitution de chaînes.



awk embarque des variables, des tableaux associatifs.
awk permet de parser un fichier, de calculer des moyennes
à la volée, plein de trucs comme ça.
gawk ajoute gensub qui le rend bien similaire à sed.

Il semble manquer l'option -i de sed.

Marc Boyer
--
À mesure que les inégalités regressent, les attentes se renforcent.
François Dubet
Avatar
Francois Lafont
Le 04/09/2012 11:25, Marc Boyer a écrit :

1. Pouvez-vous me donner l'équivalent de la commande ci-dessous mais en
utilisant awk au lieu de sed ?

sed -r
-e 's/^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$'/1/g'
fichier

La regex choisie est un exemple, ça pourrait être autre chose. En
cherchant dans la page man de awk, j'ai trouvé comme équivalent ceci :

awk '{
c=gensub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "1", "g")
print c
}' fichier

a) Est-ce que cela vous paraît être un équivalent correct ?



Oui

b) N'y a-t-il pas plus simple quand même ?



Non. Pas à ma connaissance.



Ok.

2) Dans la commande juste au dessus (celle avec awk), j'ai voulu
remplacer la fonction gensub() par gsub() mais en vain. Par exemple,
ceci :

awk '{
gsub("^var[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$", "1")
print c
}' fichier

m'affiche « 1 » au lieu de m'afficher le premier sous-groupe dans ma
regex (comme c'est le avec gensub). Pourquoi ?



Parce que c'est la spec de gsub, et que gensub a été visiblement écrit
pour pouvoir faire des correspondances de sous-chaines.



C'est dommage. Je trouve que c'est très pratique. Mais bon, s'il y
gensub tout va bien.

3) Enfin, connaissez-vous un équivalent avec awk de la commande de la
question 1 à laquelle j'ajoute l'option -i (bien pratique je trouve) ?



L'option -i est (de tête) une extention du GNU sed.
Elle n'existe visiblement pas dans le GNU awk.



Merci pour cette confirmation.

awk '{ c=gensub("regex", "remplacement", "g"); print c }'
fichier > /tmp/fichier_temp
cat fichier_temp > fichier
rm /tmp/fichier_temp

ce qui est tout de même beaucoup plus bavard.



Et encore, tu n'as fait aucun test d'échec.



Ça m'intéresserait bien de savoir à quoi ça ressemblerait avec des tests
« bétonnés de chez bétonnés » ? À ça ?

awk '{ c=gensub("regex", "remplacement", "g"); print c }'
fichier > /tmp/fichier_temp

if [ "$?" = "0" ]; then
cat fichier_temp > fichier
rm /tmp/fichier_temp
fi

Je pose la question parce que je me dis qu'au final on peut ne pas en
voir le bout des tests si on pousse la prudence à l'extrême (par
exemple, qu'est-ce qui me prouve qu'entre la commande "awk" et la
commande "cat" le fichier fichier_temp ne va pas subir une modification
intempestive de je ne sais quel programme ?).

Merci Marc pour tes précisions.

--
François Lafont
Avatar
Marc Boyer
Le 04-09-2012, Francois Lafont a écrit :
Et encore, tu n'as fait aucun test d'échec.



Ça m'intéresserait bien de savoir à quoi ça ressemblerait avec des tests
« bétonnés de chez bétonnés » ? À ça ?

awk '{ c=gensub("regex", "remplacement", "g"); print c }'
fichier > /tmp/fichier_temp



Faut déjà générer un num de fichier unique (avec mktemp ou un copain).
Ensuite, oui, tester la valeur de retour de awk (mais bon, un petit &&
devrait suffire).
Et moi, je prendrais ensuite un mv plutôt qu'un cat.
Si les deux fichiers sont sur la même partition, mv devrait ne pas
copier le contenu du fichier, mais uniquement changer des infos d'emplacement
et de nommage.

Un truc du genre (non teste):
SRC=`basename fichier`
RES=`mktemp XXX-$SRC`
BACKUP=`mktemp XXX-${SRC}~`

awk '{....}' fichier > $RES
if [ "$?" == "0" ]; then
mv fichier ${BACKUP}
mv ${RES} fichier
fi

Mais pour êre vraiment robuste, faudrait aussi tester que les mv
fonctionnent, et essayer de revenir en arrière....
On pourrait être plus prudent et faire un cp à la place du premier mv.

Je pose la question parce que je me dis qu'au final on peut ne pas en
voir le bout des tests si on pousse la prudence à l'extrême (par
exemple, qu'est-ce qui me prouve qu'entre la commande "awk" et la
commande "cat" le fichier fichier_temp ne va pas subir une modification
intempestive de je ne sais quel programme ?).



Ce problème existera aussi avec sed -i.

Marc Boyer
--
À mesure que les inégalités regressent, les attentes se renforcent.
François Dubet
1 2