Utilisation de sed et grep / egrep pour rechercher et remplacer

97

J'utilise egrep -Rsuivi d'une expression régulière contenant environ 10 unions, donc comme: .jpg | .png | .gifetc. Cela fonctionne bien, maintenant je voudrais remplacer toutes les chaînes trouvées par.bmp

Je pensais à quelque chose comme

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

donc le problème ici est de savoir comment faire une expression régulière d'union similaire dans sedet comment lui dire d'enregistrer les modifications dans le fichier dont il a obtenu l'entrée.

Ou Je
la source
2
J'ai trouvé cette question en cherchant des moyens de rechercher et de remplacer plusieurs fichiers dans une hiérarchie de répertoires. Pour les autres dans ma situation, essayez rpl .
titaniumdecoy
merci rpl fonctionne et est vraiment facile à retenir .. il suffit de rpl old_string new_string target_files.
cesarpachon

Réponses:

183

Utilisez cette commande:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep: trouver les lignes correspondantes à l'aide d'expressions régulières étendues

    • -l: liste uniquement les noms de fichiers correspondants

    • -R: recherche récursivement dans tous les répertoires donnés

    • -Z: utiliser \0comme séparateur d'enregistrement

    • "\.jpg|\.png|\.gif": correspond à l'une des chaînes ".jpg", ".gif"ou".png"

    • .: lancer la recherche dans le répertoire courant

  • xargs: exécuter une commande avec le stdin comme argument

    • -0: utiliser \0comme séparateur d'enregistrement. Ceci est important pour faire correspondre le -Zof egrepet pour éviter d'être dupé par les espaces et les retours à la ligne dans les noms de fichiers d'entrée.

    • -l: utiliser une ligne par commande comme paramètre

  • sed: Le s tream ed Itor

    • -i: remplacer le fichier d'entrée par la sortie sans faire de sauvegarde

    • -e: utilisez l'argument suivant comme expression

    • 's/\.jpg\|\.gif\|\.png/.bmp/g': remplace toutes les occurrences des chaînes ".jpg", ".gif"ou ".png"par".bmp"

David Schmitt
la source
tout fonctionne sauf le | dans la partie sed. Je ne comprends pas pourquoi car cela a du sens ... la partie -l de xargs me donnait des erreurs, alors je l'ai supprimée, est-ce que cela pourrait être lié?
Ori le
J'ai trouvé que cette commande ajoute une nouvelle ligne à la fin de tous les fichiers qu'elle traite.
titaniumdecoy
@titanumdecoy: Je n'ai pas pu reproduire ce comportement. quelle version de sed utilisiez-vous et sur quel OS êtes-vous?
David Schmitt
1
@DavidSchmitt: Vous voudrez probablement l'utiliser sed -rpour les expressions régulières étendues. À ce stade, le modèle correspondra à ce qui est utilisé dans egrep, et vous voudrez peut-être le mettre dans une variable pour le réutiliser.
bukzor
cette commande m'a sauvé des heures de travail en copiant les fichiers d'en-tête de mon application pour la bibliothèque que j'ai créée. C'est génial :) Voici la commande que j'ai utilisée egrep -lRZ "\ .h $". | xargs -0 tar -cvf headers.tar | (cp headers.tar headers; cd headers; tar xf headers.tar;)
The Lazy Coder
11

Honnêtement, tout comme j'aime sed pour les tâches appropriées, c'est définitivement une tâche pour perl - c'est vraiment plus puissant pour ce genre de one-liners, en particulier pour "l'écrire d'où il vient" (le -icommutateur de perl le fait pour vous, et vous permet également de conserver l'ancienne version, par exemple avec un .bak ajouté, utilisez simplement à la -i.bakplace).

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

plutôt qu'un travail complexe dans sed (si même possible là-bas) ou awk ...

Alex Martelli
la source
6
sed utilise -i, tout comme perl.
Stobor
@Stobor - Je jure que j'ai eu des problèmes où l'opération perl lorsque j'ai alimenté la chaîne de remplacement de regex faisait exactement ce que je voulais, contrairement à sed, même si j'ai donné l'option regex à sed.. Je pense que j'ai oublié quelques indicateurs pour sed ou il avait des limites.
meder omuraliev
10

Une autre façon de faire cela

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

Un peu d'aide concernant la commande ci-dessus

La recherche fera la recherche pour vous sur le répertoire courant indiqué par .

-name le nom du fichier dans mon cas, son pom.xml peut donner des jokers.

-exec exécuter

sed éditeur de flux

-i ignorer la casse

s est un substitut

/4.6.0.../ Chaîne à rechercher

/5.0.0.../ Chaîne à remplacer

Cyril Cherian
la source
peut-être pas aussi puissant - mais c'est beaucoup plus facile à comprendre pour moi que la réponse acceptée
icc97
5

Je n'ai pas réussi à faire fonctionner l'une des commandes de cette page: la solution sed a ajouté une nouvelle ligne à la fin de tous les fichiers qu'elle traitait, et la solution perl n'a pas pu accepter suffisamment d'arguments de find. J'ai trouvé cette solution qui fonctionne parfaitement:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

Cela reviendra dans l'arborescence de répertoires actuelle et remplacera search_regexpar replacement_stringdans tous les fichiers se terminant par .hou.m .

J'ai également utilisé rpl à cette fin dans le passé.

titane
la source
0

essayez quelque chose en utilisant une boucle for

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

pas joli, mais devrait faire.

Don Johe
la source
3
xargs est certainement plus approprié ici.
Nathan Fellman
1
et l'utilisation du |while read imodèle permettrait la diffusion en continu et éviterait les restrictions de longueur lorsque les résultats d'egrep deviennent trop longs
David Schmitt
0

Mon cas d'utilisation était que je voulais remplacer foo:/Drive_Letterpar foo:/bar/baz/xyz Dans mon cas, j'ai pu le faire avec le code suivant. J'étais dans le même emplacement de répertoire où il y avait beaucoup de fichiers.

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

espérons que cela a aidé.

MISE À JOUR s | toto: / lettre_lecteur: | toto: / ba / baz / xyz | g

neo7
la source
Vous pouvez utiliser d'autres délimiteurs pour la commande sed, ce qui rend les chemins beaucoup plus agréables:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g'
Kevin