web-dev-qa-db-fra.com

Recherche globale en ligne de commande Linux et remplacement

J'essaie de rechercher et de remplacer une chaîne dans tous les fichiers correspondant à grep sur une machine linux. J'ai quelques morceaux de ce que je veux faire, mais je ne suis pas sûr de la meilleure façon de les enchaîner.

grep -n 'foo' * me donnera la sortie sous la forme:

[filename]:[line number]:[text]

Pour chaque fichier renvoyé par grep, j'aimerais remplacer "foo" par "bar" et écrire le résultat dans le fichier. Y a-t-il un bon moyen de faire ça? Peut-être un pipeline de fantaisie?

79
Michael Kristofik

Voulez-vous dire rechercher et remplacer une chaîne dans tous les fichiers correspondant à grep?

Perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`

Éditer

Puisque cela semble être une question assez populaire, je pensais que je mettrais à jour.

De nos jours, j'utilise principalement ack-grep comme c'est plus convivial. Donc, la commande ci-dessus serait:

Perl -p -i -e 's/old/new/g' `ack -l searchpattern`

Pour gérer les espaces dans les noms de fichiers, vous pouvez exécuter:

ack --print0 -l searchpattern | xargs -0 Perl -p -i -e 's/old/new/g'

vous pouvez faire plus avec ack-grep. Supposons que vous souhaitiez limiter la recherche aux fichiers HTML uniquement:

ack --print0 --html -l searchpattern | xargs -0 Perl -p -i -e 's/old/new/g'

Et si l'espace blanc n'est pas un problème, c'est encore plus court:

Perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
Perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files
70
armandino

Cela semble être ce que vous voulez, d'après l'exemple que vous avez donné:

sed -i 's/foo/bar/g' *

Ce n'est pas récursif (il ne descendra pas dans les sous-répertoires). Pour une solution intéressante en remplaçant dans des fichiers sélectionnés tout au long d’un arbre, j’utiliserais:

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

Le *.html est l'expression à laquelle doivent correspondre les fichiers, le .bak après le -i fait une copie du fichier original, avec une extension .bak (cela peut être n'importe quelle extension) et le g à la fin de l'expression sed indique à sed de remplacer plusieurs copies sur une ligne (plutôt que seulement le premier). Le -print trouver est pratique pour montrer quels fichiers ont été mis en correspondance. Tout cela dépend des versions exactes de ces outils sur votre système.

108
MattJ

Si votre sed(1) a un -i _ option, utilisez-le comme ceci:

for i in *; do
  sed -i 's/foo/bar/' $i
done

Sinon, il y a plusieurs façons de varier les options suivantes en fonction de la langue avec laquelle vous voulez jouer:

Ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
Perl -pi.bak -e 's/foo/bar/' *
13
Keltia

J'aime et utilise la solution ci-dessus ou une recherche à l'échelle du système et remplace parmi des milliers de fichiers:

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;

Je suppose avec le '* .htm?' au lieu de .html, il recherche et trouve les fichiers .htm et .html de la même manière.

Je remplace le .bak par le tilde (~) plus répandu à l’échelle du système pour faciliter le nettoyage des fichiers de sauvegarde.

6
hans

Cela fonctionne en utilisant grep sans avoir besoin d'utiliser Perl ou find.

grep -rli 'old-Word' * | xargs -i@ sed -i 's/old-Word/new-Word/g' @
4
pymarco

find . -type f -print0 | xargs -0 <sed/Perl/Ruby cmd> traitera simultanément plusieurs noms de fichiers contenant de l’espace, chargeant un interprète par lot. Plus vite.

3
koolb

C'est en fait plus facile qu'il n'y parait.

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grep revient dans votre arborescence (-R) et affiche uniquement le nom du fichier (-l), en commençant par le répertoire actuel (./)
  • qui est redirigé vers xargs, qui les traite un à la fois (-n 1) et utilise% comme espace réservé (-I%) dans une commande Shell (sh -c)
  • dans la commande Shell, le nom du fichier est d'abord imprimé (ls%;)
  • sed effectue ensuite une opération en ligne (-i), une substitution ('s /') de foo avec barre (foo/bar), globalement (/ g) sur le fichier (à nouveau, représenté par%)

Peasy facile. Si vous maîtrisez parfaitement find, grep, xargs, sed et awk, rien n’est impossible en ce qui concerne la manipulation de fichiers texte en bash :)

1
siliconrockstar

La réponse déjà donnée d'utiliser find et sed

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

est probablement la réponse standard. Ou vous pouvez utiliser Perl -pi -e s/foo/bar/g' À la place de la commande sed.

Pour les utilisations les plus rapides, vous pouvez trouver que la commande rpl est plus facile à mémoriser. Voici remplacement (foo -> bar), de manière récursive sur tous les fichiers du répertoire en cours:

rpl -R foo bar .

Il n'est pas disponible par défaut sur la plupart des distributions Linux mais son installation est rapide (apt-get install rpl Ou similaire).

Toutefois, pour les travaux plus difficiles qui impliquent des expressions régulières et des substitutions de dos, ou des renommages de fichiers ainsi que des opérations de recherche et remplacement, l'outil le plus général et le plus puissant dont je dispose est repren , un petit Python que j'ai écrit il y a quelque temps pour certaines tâches de renommage et de refactoring plus épineuses. Les raisons pour lesquelles vous pourriez le préférer sont les suivantes:

  • Prend en charge le changement de nom des fichiers ainsi que la recherche et le remplacement du contenu des fichiers (y compris le déplacement de fichiers entre répertoires et la création de nouveaux répertoires parents).
  • Consultez les modifications avant de vous engager à effectuer la recherche et le remplacement.
  • Prend en charge les expressions régulières avec la substitution arrière, les mots entiers, les majuscules et minuscules et les modes de préservation de la casse (remplacer foo -> bar, Foo -> Bar, FOO -> BAR).
  • Fonctionne avec plusieurs remplacements, y compris des échanges (foo -> bar et bar -> foo) ou des ensembles de remplacements non uniques (foo -> bar, f -> x).

Consultez le fichier README pour des exemples.

1
jlevy