Bonjour,
Un drôle de comportement, je dois modifier des fichiers, je fais pour celà des petits scripts.
Je veux supprimer une ligne contenant une expression :
grep -v "\#include \"fichier.h\"" $1 > $1
me rend un fichier vide. Je suis obligé de faire :
grep -v "\#include \"fichier.h\"" $1 > $1.1
mv $1.1 $1
Pourtant juste avant j'ai fait un ligne du genre :
sed -e 's~\#include \"fichier.h\"~~g' $1 > $1 ;
Qui fonctionne.
Pourquoi cette différence de comportement pour un résultat somme toute similaire ?
# Peut-être... mais à vérifier
Posté par mekare . Évalué à 2.
[^] # Re: Peut-être... mais à vérifier
Posté par nicoastro . Évalué à 1.
# sed -i
Posté par left . Évalué à 2.
sed -i -e 's~\#include \"fichier.h\"~~g' $1
[^] # Re: sed -i
Posté par nicoastro . Évalué à 1.
[^] # Re: sed -i
Posté par Jacques L'helgoualc'h (site web personnel) . Évalué à 3.
Sed, lui, est un éditeur ;) --- pour GNU Sed, info sed est plus complet que la page de manuel. Voir aussi http://sed.sf.net/ ...
Les deux commandes ci-dessus ne sont pas strictement équivalentes, grep -v 'regexp' supprimant les lignes quand sed -e 's/regexp//' se contente de supprimer le motif reconnu, et donc donne au moins une ligne vide. L'équivalent serait plutôt sed -e '/regexp/d' (aux nuances d'écriture de la regexp près).
L'ouverture en lecture et écriture du même fichier est à proscrire totalement.
[^] # Re: sed -i
Posté par ✅ ffx . Évalué à 4.
Peu importe que sed soit un éditeur ou pas, normalement c'est au niveau du shell que la redirection est évaluée.
En lisant le "> $1", le shell doit (re)créer (donc vider dans le cas où il existe déjà) le fichier $1, puis il exécute la commande demandée tout en redirigeant la sortie vers le fichier qu'il a ouvert.
Bref, ca ne devrait jamais marcher, peu importe la commande, puisque c'est au niveau du shell que le problème se produit.
D'ailleurs en faisant le test de mon côté, j'obtiens bien un fichier vide tant avec grep qu'avec sed.
Mes tests ont été réalisés avec bash, peut-être que Nicolas utilise un autre shell ?
Sinon je ne comprends pas...
[^] # Re: sed -i
Posté par netsurfeur . Évalué à 6.
Tu as raison, le shell traite les redirections AVANT de lancer la commande.
Une commande shell de la forme suivante:
cmd fichier > fichier
Se traduit donc par la séquence suivante:
1) Le shell crée un nouveau process (appel système fork)
2) Il ouvre fichier et tronque son contenu (le fichier est donc maintenant VIDE)
3) Il affecte le descripteur de fichier obtenu en 2) à la sortie standard
4) il lance cmd fichierqui se contente d'écrire dans sa sortie standard (appel système exec)
J'ai donc beaucoup de doutes surt le fait que sed -e 's~\#include \"fichier.h\"~~g' $1 > $1 fonctionne !
On peut par contre jouer avec l'ordre d'évaluation des redirections par le shell pour ne pas avoir à utiliser un nom de fichier intermédiaire:
(rm $1; grep -v "\#include \"fichier.h\"" > $1) < $1
[^] # Re: sed -i
Posté par Jacques L'helgoualc'h (site web personnel) . Évalué à 1.
Dans ta description, tu ne précises pas que dans cmd fichier , c'est cmd qui s'occupe d'ouvrir son fichier argument --- mais ne sait pas où va finalement sa sortie; une variante est cmd <fichier , où c'est le shell qui met aussi en place l'entrée.
Il se pourrait que la lecture du ou des premiers blocs se fasse parfois avant l'ouverture effective en écriture, suivant comment le noyau ordonnance ses opérations.
Avec le rm $1, un seul nom est utilisé, mais la sortie doit se faire vers un autre inode, celui de la lecture continuant à pointer sur l'ancien, qui n'est pas réalloué tant que l'opération de lecture n'est pas terminée, même si le nom est déjà effacé... Cette méthode ne fonctionnera pas « bien » quand il y a plusieurs liens durs vers le même inode.
[^] # Re: sed -i
Posté par netsurfeur . Évalué à 2.
Je voulais juste décrire les étapes principales pour expliquer que la redirection vide le fichier avant que cmd commence à s'en servir.
Il se pourrait que la lecture du ou des premiers blocs se fasse parfois avant l'ouverture effective en écriture, suivant comment le noyau ordonnance ses opérations.
La séquence que j'ai décrite est déterministe, le fichier DOIT être tronqué avant d'être lu.
Si quelqu'un constate que ça ne se passe pas comme ça, il a trouvé un BUG noyau...
Avec le rm $1, un seul nom est utilisé, mais la sortie doit se faire vers un autre inode
Exactement, c'est pour ça que j'ai parlé de nom de fichier intermédiaire. Je n'ai pas dit que l'écriture se faisait en place.
[^] # Re: sed -i
Posté par ✅ ffx . Évalué à 3.
Si quelqu'un constate que ça ne se passe pas comme ça, il a trouvé un BUG noyau...
Hmm, pas forcément, les shells sont de plus en plus perfectionnés et peut-être que certains reconnaissent que la commande utilise le même fichier en entrée et sortie ?
Typiquement, un truc qui m'a énervé récemment c'est quand j'ai voulu faire un gunzip sur un fichier sans extension .gz, eh bien la complétion automatique ne fonctionnait tout simplement pas ! (à bas le progrès !)
[^] # Re: sed -i
Posté par nicoastro . Évalué à 2.
En fait dans mon soucis de donner un exemple minimaliste j'ai omis de le tester... et effectivement même le sed écrase de fichier.
Donc voici l'exemple réel qui me donne le résultat sans écraser le fichier :
for i in $1/Makefile* ; do
sed -e 's~INCDIR = $(OEDIR)/include~~g' $i \
| sed -e 's~LIBDIR = $(OEDIR)/lib~~g' \
> $i ;
done
Ceci OBLIGATOIREMENT dans un fichier script (j'ai essayé de façon intéractive, avec ou sans les '\' ça ne fonctionne pas).
PS : c'est bash que j'utilise.
[^] # Re: sed -i
Posté par Jacques L'helgoualc'h (site web personnel) . Évalué à 1.
Pour répondre aussi à Kerro, Sed = stream editor : c'est un éditeur de flux, au code très comapct, qui s'occupe surtout de traiter son flux de données. L'option -i est juste un cadeau aux fainéants pour ne pas avoir à gérer le fichier temporaire (et, accessoirement, leur évite de se tirer une balle dans le pied avec <fic >fic ). On trouvera des discussions plus approfondies sur les redirections du shell sur news:fr.comp.os.unix, ou dans l'Advanced Bash-scripting Guide (traduit en français).
# Les sources sont reines dans ton cas
Posté par Kerro . Évalué à 2.
Sed est un éditeur de texte (de flux pour être exact). Il est peut-être conçu pour résister au mauvais traitement que tu lui appliques. Grep n'a rien à voir avec l'édition de texte. La différence est peut-être là.
Ca ne fonctionnera peut-être plus avec sed si au lieu de supprimer tu fais un remplacement par une chaîne plus longue. Le fait que tes fichiers soient petits entrent peut-être également en ligne de compte si sed utilise un tampon plus grand que tes fichiers.
Si tu veux la vraie réponse à ta question, regardes dans les sources de grep, mais ça ne t'avanceras pas beaucoup, surtout en comparaison du temps nécessaire :-)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.