OVH Cloud OVH Cloud

script avec diff, grep et sed

16 réponses
Avatar
Lionel GRUHN
bonjour à tous

J'ai fait un petit script dont le but est de me donner la liste des fichiers
qui sont dans un répertoire new mais pas dans le répertoire old. Le contenu
de ces fichiers ne m'intéresse pas; les répertoires contiennent environ
2000 fichiers.



#! /bin/bash

# pipe avec sort pour etre sur d'etre dans l'ordre alphabetique
ls -1 old | sort > ls.old
ls -1 new | sort > ls.new

# normalement, pas de pb avec la casse des noms de fichier mais mefiance...
# je ne veux que les lignes commencant par "> " (ajouts dans new)
# je ne veux pas que ces caractères figurent dans le fichier final
diff -i ls.old ls.new | grep '^> ' | sed "s/^> //" > ls.diff

# supprimer les fichiers intermediaires
rm -f ls.old
rm -f ls.new




Le script fonctionne et je cherche à l'optimiser (plus par principe que par
nécessité):
- le tri de la sortie de ls est-il vraiment nécessaire?
- je voudrais supprimer l'usage de fichiers temporaires (ls.old et ls.new),
mais je ne sais pas rediriger la sortie de mes 2 ls vers l'entrée de diff.
Si je donne directement les répertoires à diff, celui-ci va me comparer le
contenu des fichiers (je n'ai pas trouvé d'option correspondant à ce que je
veux).
- existe-t-il un moyen de réduire la chaîne de pipes diff | grep | sed ?
- aucun de mes noms de fichiers ne contient d'espace, mais j'ai fait
quelques tests et le script fonctionne quand même. A priori, il me semble
que celà doit toujours être le cas, pouvez-vous me le confirmer?


Merci d'avance

Lionel Gruhn, programmeur du dimanche

10 réponses

1 2
Avatar
Nicolas George
Lionel GRUHN wrote in message :
J'ai fait un petit script dont le but est de me donner la liste des fichiers
qui sont dans un répertoire new mais pas dans le répertoire old. Le contenu
de ces fichiers ne m'intéresse pas; les répertoires contiennent environ
2000 fichiers.


Pourquoi pas plus simplement (en zsh, c'est plus pénible à écrire en sh, et
surtout j'ai la flemme de lire le man pour savoir _comment_ le lire) :

for i in new/*(:t);
[[ -e old/$i ]] || print -r -- $i

 ?

- existe-t-il un moyen de réduire la chaîne de pipes diff | grep | sed ?


sed peut émuler le fonctionnement de grep, avec quelque chose comme
'/^> /s///' et l'option -n.

- aucun de mes noms de fichiers ne contient d'espace, mais j'ai fait
quelques tests et le script fonctionne quand même. A priori, il me semble
que celà doit toujours être le cas, pouvez-vous me le confirmer?


Ça ne va pas marcher s'il y a des retours à la ligne, ou éventuellement des
caractères considérés par ls comme non-imprimables (là ça risque de dépendre
des versions de ls ; d'une manière générale, éviter de parser la sortie de
ls, ce n'est pas fait pour).

Avatar
Lionel GRUHN
Nicolas George wrote:
Pourquoi pas plus simplement (en zsh, c'est plus pénible à écrire en sh,
et surtout j'ai la flemme de lire le man pour savoir _comment_ le lire) :

for i in new/*(:t);
[[ -e old/$i ]] || print -r -- $i


Effectivement, je n'avais pas pensé à effectuer ce genre de test... Voilà
qui va simplifier énormément mon script! Et hop, retour à la case doc de
bash ;o)

Pour zsh... disons que le gain d'une "migration" se situe à trop long terme
pour l'utilisation que je fais de mon ordi...




sed peut émuler le fonctionnement de grep, avec quelque chose comme
'/^> /s///' et l'option -n.


Voilà une information du plus grand intérêt: peut-être pas pour ce script
(voir plus haut), mais direction la doc de sed!




Ça ne va pas marcher s'il y a des retours à la ligne, ou éventuellement
des caractères considérés par ls comme non-imprimables (là ça risque de
dépendre des versions de ls ; d'une manière générale, éviter de parser la
sortie de ls, ce n'est pas fait pour).


Quelle drôle idée: des retours chariots dans un nom de fichier! Enfin, quand
on voit tout ce qui traîne comme nom de pièces jointes de mails... Après
tout, mieux vaut se méfier! Je note le conseil.


Merci beaucoup!

Lionel

Avatar
Rakotomandimby (R12y) Mihamina
( Tue, 08 Mar 2005 22:20:06 +0100 ) Lionel GRUHN :

Effectivement, je n'avais pas pensé à effectuer ce genre de test... Voilà
qui va simplifier énormément mon script! Et hop, retour à la case doc de
bash ;o)


c'est du zsh qu'il t'as donné là...

sinon si tu n'es pas un mega utilisateur de shell, tu peux passer de bash
en zsh sans meme t'en rendre compte... pour les truc banaux (pluriel de
banal), ce qui passe en bash passe en zsh (du moins chez moi).

--
L'ASPO a pour but de démocratiser l'acces a l'informatique. Une de ses
activité est l'infogerance (http://aspo.rktmb.org/activites/infogerance)
Tél: + 33 2 38 04 26 04 ou + 33 6 33 26 13 14 (France)

Avatar
Lionel GRUHN
Nicolas George wrote:
J'ai fait un petit script dont le but est de me donner la liste des
fichiers qui sont dans un répertoire new mais pas dans le répertoire old.
Le contenu de ces fichiers ne m'intéresse pas; les répertoires
contiennent environ 2000 fichiers.


Pourquoi pas plus simplement (en zsh, c'est plus pénible à écrire en sh,
et surtout j'ai la flemme de lire le man pour savoir _comment_ le lire) :

for i in new/*(:t);
[[ -e old/$i ]] || print -r -- $i

?


Et bien voilà la nouvelle version du script:


#! /bin/bash

for i in new/* ; do

# virer le chemin pour ne garder que le nom du fichier
j=${i#new/}

if [ ! -e "old/$j" ] ; then echo $j ; fi

done




A l'utilisation, il ne reste qu'à rediriger la sortie vers le fichier voulu
et le tour est joué.


comparaison des 2 scripts:

bash-2.05b$ time ./change
real 0m0.119s
user 0m0.024s
sys 0m0.008s
bash-2.05b$ time ./change2 > ls.diff
real 0m0.044s
user 0m0.035s
sys 0m0.009s

Il n'y a pas photo!!!


Du coup, je vais en faire une version où je passe en paramètres le nom des
répertoires à vérifier, histoire de pouvoir le réutiliser...

Merci beaucoup pour l'idée

Lionel


Avatar
Nicolas George
Lionel GRUHN wrote in message :
j=${i#new/}


Utilisation d'une variable non protégée : avec zsh c'est sans risque, avec
sh c'est suicidaire. Avec bash je ne sais pas.

if [ ! -e "old/$j" ] ; then echo $j ; fi


Utilisation de echo : non-fiable dès qu'il y a des dans le mot, ou un - en
début. Privilégier printf '%s' dans ce genre de cas.

Avatar
Lionel GRUHN
Rakotomandimby (R12y) Mihamina wrote:

Effectivement, je n'avais pas pensé à effectuer ce genre de test... Voilà
qui va simplifier énormément mon script! Et hop, retour à la case doc de
bash ;o)


c'est du zsh qu'il t'as donné là...


J'avais bien compris... Mais c'est l'idée de tester l'existence du fichier
dans le répertoire old qui m'a semblé intéressante car elle supprimait
l'usage de ls, diff, grep et sed. L'adaptation pour bash n'a pas été très
longue, ni très difficile, même si je suis sûr qu'elle a dû faire hurler
les professionnels!




sinon si tu n'es pas un mega utilisateur de shell, tu peux passer de bash
en zsh sans meme t'en rendre compte...


Les quelques questions que j'ai eu à poser (ici ou ailleurs) ont toujours
trouvé une réponse différente en fonction du shell utilisé, je serais donc
devenu sans le savoir un méga utilisateur de shell ? ;o)

J'ai choisi bash principalement par facilité: doc abondante et accessible
par un amateur, installé par défaut sur toutes les distrib que j'ai
utilisées... J'estime qu'un changement de shell ne s'impose pas dans mon
cas: l'investissement en temps serait beaucoup trop lourd pour les avantage
que je pourrais espérer en tirer.


Avatar
Lionel GRUHN
Nicolas George wrote:

Lionel GRUHN wrote in message :
j=${i#new/}


Utilisation d'une variable non protégée : avec zsh c'est sans risque, avec
sh c'est suicidaire. Avec bash je ne sais pas.


Des caractères "loufoques" dans i pourraient planter mon truc, c'est ce que
tu veux dire?





if [ ! -e "old/$j" ] ; then echo $j ; fi


Utilisation de echo : non-fiable dès qu'il y a des dans le mot, ou un -
en début. Privilégier printf '%s' dans ce genre de cas.


C'est vrai que j'ai déjà eu des pb avec des noms de fichiers commençant par
des !, des # ou des - dans d'autres circonstances (rm ou mv par exemple).
Retour à la doc puis modif du script...

Merci beaucoup

Lionel


Avatar
Laurent Wacrenier
Lionel GRUHN écrit:
- le tri de la sortie de ls est-il vraiment nécessaire?


Par défaut, "ls" trie les fichiers.

- je voudrais supprimer l'usage de fichiers temporaires (ls.old et ls.new),
mais je ne sais pas rediriger la sortie de mes 2 ls vers l'entrée de diff.
Si je donne directement les répertoires à diff, celui-ci va me comparer le
contenu des fichiers (je n'ai pas trouvé d'option correspondant à ce que je
veux).


Il faut au moins garder un. L'autre peut se lire sur l'entrée standart.

- existe-t-il un moyen de réduire la chaîne de pipes diff | grep | sed ?


ls old > ls.old
ls new | diff -i - ls.old | sed -n "s/^> //p" > ls.diff

Ceci dit, c'est mieux de faire ça avec "join".

Avatar
Lionel GRUHN
Laurent Wacrenier wrote:

Lionel GRUHN écrit:
- le tri de la sortie de ls est-il vraiment nécessaire?


Par défaut, "ls" trie les fichiers.


Il me semblait bien, mais comme je n'avais pas trouvé de confirmation dans
le man... J'ai dû le lire en diagonale ;o)




Il faut au moins garder un. L'autre peut se lire sur l'entrée standart.


Ben oui, bien sûr! Ne pouvant me passer des 2 en même temps, j'ai tout
bêtement zappé l'idée de n'en supprimer qu'un!




Ceci dit, c'est mieux de faire ça avec "join".


Effectivement, join -v fait tout le travail tout seul! Je ne connaissais
pas...

ls new | join -v 1 - ls.old > ls.diff


Merci beaucoup

Lionel


Avatar
Laurent Wacrenier
Lionel GRUHN écrit:
Il faut au moins garder un. L'autre peut se lire sur l'entrée standart.


Ben oui, bien sûr! Ne pouvant me passer des 2 en même temps, j'ai tout
bêtement zappé l'idée de n'en supprimer qu'un!


Avec "ls" direct, il faut en garder un. Mais avec find, il y a peut
être moyen de se passer de fichier temporaire :

find old new -type f | sort -t / -k 2 | ...


1 2