75 votes

Utilisation de grep et sed pour trouver et remplacer une chaîne de caractères

J'utilise ce qui suit pour rechercher récursivement dans un répertoire une chaîne spécifique et la remplacer par une autre :

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g'

Cela fonctionne bien. Le seul problème est que si la chaîne de caractères n'existe pas alors sed échoue car il ne reçoit aucun argument. C'est un problème pour moi, car je l'exécute automatiquement avec ANT et la construction échoue parce que sed échoue.

Existe-t-il un moyen de le rendre infaillible au cas où la chaîne ne serait pas trouvée ?

Je suis intéressé par une solution simple en une seule ligne que je peux utiliser (pas nécessairement avec grep o sed mais avec des commandes Unix courantes comme celles-ci).

0 votes

La raison pour laquelle je veux garder cela aussi simple que possible est que mon script se connecte à un serveur distant et exécute cette ligne avec SSH. si j'utilisais un shell script pour cela, je devrais copier le shell script sur le serveur avant et ensuite l'exécuter là. j'essaie d'éviter cela et de garder cela simple. merci.

1 votes

1 votes

En venant ici depuis Google, je cherchais la chaîne exacte donnée dans la question ! (Je n'ai pas eu le problème dont souffre le PO)

78voto

Michael Berkowski Points 137903

Vous pouvez utiliser find y -exec directement dans sed plutôt que de localiser d'abord oldstr con grep . C'est peut-être un peu moins efficace, mais ce n'est peut-être pas important. De cette façon, le sed est exécuté sur tous les fichiers listés par find mais si oldstr n'est pas là, il est évident qu'il ne fonctionnera pas dessus.

find /path -type f -exec sed -i 's/oldstr/newstr/g' {} \;

1 votes

C'est le cas, parce que vous exécutez un programme séparé. sed pour chaque fichier au lieu de laisser xargs les mettre en lot. Cela dit, à moins que vous ne parliez de plusieurs milliers de petits fichiers, il est peu probable que vous remarquiez une différence.

0 votes

@geekosaur : Oh, c'est vrai Je n'ai pas pensé à un certain nombre d'exécutions que shell devrait faire. Bonne remarque !

0 votes

Merci, j'ai essayé votre solution. cela a résolu mon problème de valeur de retour, maintenant il retourne 0 quand il ne trouve rien. le problème est qu'il retourne : sed: no input files même si j'ai des fichiers avec l'ancienne chaîne dans le répertoire. vous savez pourquoi ?

16voto

jm666 Points 17312

Votre solution est correcte. Essayez-la seulement de cette façon :

files=$(grep -rl oldstr path) && echo $files | xargs sed....

donc exécuter le xargs seulement quand grep retourne 0 par exemple, lorsqu'on trouve la chaîne de caractères dans certains fichiers.

2 votes

C'est une belle solution sous-estimée ! C'est ce dont j'avais besoin pour exécuter sed sur une liste de fichiers en sortie de grep, et éviter l'erreur : "can't read .... No such file or directory". Merci pour votre réponse !

3 votes

D'accord avec @Leopoldo. Cela semble être le plus Posixy et portable (pour des OS comme Solaris) et celui qui a le moins d'effets secondaires (ne pas toucher à chaque fichier mtime, etc). files=$(grep -rl oldstr path | cut -f 1 -d ':' | sort | uniq) devrait construire une liste plus petite où les fichiers sont listés une fois.

9voto

Michael Points 3132

J'ai repris l'idée de Vlad et l'ai un peu modifiée. Au lieu de

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null

Ce qui donne

sed: couldn't edit /dev/null: not a regular file

Je fais 3 connexions différentes au serveur distant

touch deleteme
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' ./deleteme
rm deleteme

Bien que cette méthode soit moins élégante et qu'elle nécessite deux connexions supplémentaires au serveur (il existe peut-être un moyen de tout faire en une seule ligne), elle fait aussi bien le travail.

0 votes

Et ce script m'a sauvé la vie !

8voto

geekosaur Points 26170

Standard xargs n'a pas de bon moyen de le faire ; il vaut mieux utiliser find -exec comme quelqu'un d'autre l'a suggéré, ou envelopper le sed dans un script qui ne fait rien s'il n'y a pas d'arguments. GNU xargs a le --no-run-if-empty et l'option BSD / OS X xargs a le -L qui devrait faire quelque chose de similaire.

0 votes

Merci, j'ai essayé d'utiliser --no-run-if-empty mais il renvoie toujours un code non nul (il renvoie 1) et cela déclencherait également un échec de la construction pour moi. à quel point est-il générique et courant que find commande ?

0 votes

find -exec remonte à la 7e édition de la recherche UNIX ; il devrait fonctionner partout où l'on dispose d'un système de gestion de l'information. find installé.

1 votes

+1 Je suis surpris que le xargs -r fix n'est pas mentionné dans d'autres réponses. Pour ce cas d'utilisation limité, c'est simple et suffisant.

4voto

Je pense que sans utiliser -exec vous pouvez simplement fournir /dev/null comme au moins un argument au cas où rien n'est trouvé :

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null

2 votes

Pourrait être une solution de contournement très soignée. Malheureusement, j'obtiens sed: couldn't edit /dev/null: not a regular file :)

0 votes

@michael : Il s'avère que l'exécution de quelque chose comme ça va en fait planter le shell à cause de trop d'arguments... Voir superuser.com/questions/257250/

0 votes

Il y a beaucoup de fichiers dans mon répertoire mais seulement quelques-uns d'entre eux contiennent la chaîne de caractères que je veux changer. donc je ne m'inquiète pas trop du nombre d'arguments pour sed.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X