Comment faire une recherche / remplacement récursive d’une chaîne avec awk ou sed?

Comment trouver et remplacer chaque occurrence de:

subdomainA.example.com 

avec

 subdomainB.example.com 

dans chaque fichier texte sous l’arborescence de répertoire /home/www/ récursivement?

Remarque : N’exécutez pas cette commande sur un dossier comprenant un repository git – les modifications apscopes à .git pourraient corrompre votre index git.

 find /home/www -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' 

De l’ man find :

-print0 (GNU find only) indique find pour utiliser le caractère nul (\ 0) au lieu des espaces comme délimiteur de sortie entre les chemins trouvés. C’est une option plus sûre si vos fichiers peuvent contenir des espaces ou d’autres caractères spéciaux. Il est recommandé d’utiliser l’argument -print0 pour trouver si vous utilisez la commande -exec ou xargs (l’argument -0 est nécessaire dans xargs).

Remarque : N’exécutez pas cette commande sur un dossier comprenant un repository git – les modifications apscopes à .git pourraient corrompre votre index git.

 find /home/www/ -type f -exec \ sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} + 

Comparé à d’autres réponses, cela est plus simple que la plupart et utilise sed au lieu de perl, ce que demandait la question initiale.

La façon la plus simple pour moi est

 grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g' 

Tous les trucs sont presque les mêmes, mais j’aime bien celui-ci:

 find  -type f -exec sed -i 's///g' {} + 
  • find : cherchez dans le répertoire.

  • -type f :

    Le fichier est de type: fichier normal

  • -exec command {} + :

    Cette variante de l’action -exec exécute la commande spécifiée sur les fichiers sélectionnés, mais la ligne de commande est créée en ajoutant chaque nom de fichier sélectionné à la fin. le nombre total d’appels de la commande sera bien inférieur au nombre de fichiers correspondants. La ligne de commande est construite de la même manière que xargs construit ses lignes de commande. Une seule instance de {} est autorisée dans la commande. La commande est exécutée dans le répertoire de départ.

 cd /home/www && find . -type f -print0 | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' 

Pour moi, la solution la plus simple à retenir est https://stackoverflow.com/a/2113224/565525 , à savoir:

 sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f) 

NOTE : -i '' résout le problème OSX sed: 1: "...": invalid command code .

REMARQUE : S’il y a trop de fichiers à traiter, vous obtiendrez une Argument list too long . La solution de contournement – utilisez la solution find -exec ou xargs décrite ci-dessus.

Pour quiconque utilise un chercheur d’argent ( ag )

 ag SearchSsortingng -l0 | xargs -0 sed -i 's/SearchSsortingng/Replacement/g' 

Dans la mesure où ag ignore par défaut les fichiers / dossiers git / hg / svn, cela peut être exécuté en toute sécurité dans un référentiel.

Pour réduire les fichiers à récursivement sed , vous pouvez rechercher votre instance de chaîne:

 grep -rl  /path/to/folder | xargs sed -is^^^g 

Si vous exécutez man grep vous remarquerez que vous pouvez également définir un --exlude-dir="*.git" si vous voulez omettre de chercher dans les répertoires .git, en évitant les problèmes d’index git comme d’autres l’ont fait poliment.

Vous conduisant à:

 grep -rl --exclude-dir="*.git"  /path/to/folder | xargs sed -is^^^g 

Un oneeliner sympa en extra. Utiliser git grep

 git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g" 

Celui-ci est compatible avec les référentiels git, et un peu plus simple:

Linux:

 git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g' 

Mac:

 git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g' 

(Merci à http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-ssortingngs-in-files-in-your-git-repository/ )

J’avais juste besoin de cela et je n’étais pas satisfait de la rapidité des exemples disponibles. Donc, je suis venu avec le mien:

 cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' 

Ack-grep est très efficace pour trouver des fichiers pertinents. Cette commande a remplacé ~ 145 000 fichiers en un clin d’œil alors que les autres ont mis tellement de temps que je ne pouvais plus attendre jusqu’à la fin.

 find /home/www/ -type f -exec perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} + 

find /home/www/ -type f liste tous les fichiers dans / home / www / (et ses sous-répertoires). L’indicateur “-exec” indique find pour exécuter la commande suivante sur chaque fichier trouvé.

 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} + 

est la commande exécutée sur les fichiers (plusieurs à la fois). Le {} est remplacé par les noms de fichiers. Le + à la fin de la commande indique find pour générer une commande pour de nombreux noms de fichiers.

Par la page de manuel de find : “La ligne de commande est construite de la même manière que xargs construit ses lignes de commande.”

Il est donc possible d’atteindre votre objective (et de gérer les noms de fichiers contenant des espaces) sans utiliser xargs -0 ou -print0 .

grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done

J’imagine que la plupart des gens ne savent pas qu’ils peuvent transférer quelque chose dans un fichier “while read” et cela évite ces arguments -print0 désagréables, tout en conservant des espaces dans les noms de fichiers.

L’ajout d’un echo avant le sed vous permet de voir quels fichiers changeront avant de le faire.

Vous pouvez utiliser awk pour résoudre ceci comme ci-dessous,

 for file in `find /home/www -type f` do awk '{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}' $file > ./tempFile && mv ./tempFile $file; done 

J’espère que ceci vous aidera !!!

Essaye ça:

 sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *` 
 #!/usr/local/bin/bash -x find * /home/www -type f | while read files do sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p') if [ "${sedtest}" ] then sed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmp mv "${files}".tmp "${files}" fi done 

Si cela ne vous dérange pas d’utiliser vim avec grep ou de find outils, vous pouvez suivre la réponse donnée par l’utilisateur Gert dans ce lien -> Comment faire un remplacement de texte dans une hiérarchie de grands dossiers? .

Voici le deal:

  • grep récursivement pour la chaîne que vous souhaitez remplacer dans un certain chemin, et ne prenez que le chemin complet du fichier correspondant. (ce serait la $(grep 'ssortingng' 'pathname' -Rl) .

  • (facultatif) si vous voulez faire une pré-sauvegarde de ces fichiers dans un répertoire centralisé, vous pouvez également utiliser ceci: cp -iv $(grep 'ssortingng' 'pathname' -Rl) 'centralized-directory-pathname'

  • ensuite vous pouvez éditer / remplacer à volonté dans vim suivant un schéma similaire à celui fourni sur le lien donné:

    • :bufdo %s#ssortingng#replacement#gc | update

juste pour éviter de changer aussi

  • NearlysubdomainA.example.com
  • sous-domaineA.exemple.comp.autres

mais rest

  • sous-domaineA.exemple.com.IsIt.good

(peut-être pas bon dans l’idée de la racine du domaine)

 find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \; 

Selon cet article de blog:

 find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;' 

Pour Qshell (qsh) sur IBMi, pas bash comme balisé par OP.

Limitations des commandes qsh:

  • find n’a pas l’option -print0
  • xargs n’a pas l’option -0
  • sed n’a pas l’option -i

Ainsi la solution dans qsh:

  PATH='your/path/here' SEARCH=\'subdomainA.example.com\' REPLACE=\'subdomainB.example.com\' for file in $( find ${PATH} -P -type f ); do TEMP_FILE=${file}.${RANDOM}.temp_file if [ ! -e ${TEMP_FILE} ]; then touch -C 819 ${TEMP_FILE} sed -e 's/'$SEARCH'/'$REPLACE'/g' \ < ${file} > ${TEMP_FILE} mv ${TEMP_FILE} ${file} fi done 

Mises en garde:

  • La solution exclut la gestion des erreurs
  • Pas Bash comme marqué par OP

Si vous voulez utiliser ceci sans détruire complètement votre repository SVN, vous pouvez dire “trouver” pour ignorer tous les fichiers cachés en procédant comme suit:

 find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g' 

Combinaison de grep et de sed

 for pp in $(grep -Rl looking_for_ssortingng) do sed -i 's/looking_for_ssortingng/something_other/g' "${pp}" done 

Un peu vieille école mais cela a fonctionné sur OS X.

Il y a peu de ruses:

• Ne modifiera que les fichiers avec l’extension .sls dans le répertoire en cours

. doit être échappé pour s’assurer que sed ne les évalue pas comme “n’importe quel personnage”

, est utilisé comme délimiteur sed place de l’habituel /

Notez également que cela consiste à éditer un modèle Jinja pour passer une variable dans le chemin d’une import (mais ceci est hors sujet).

Tout d’abord, vérifiez que votre commande sed fait ce que vous voulez (cela n’imprimera que les modifications apscopes à stdout, cela ne changera pas les fichiers):

 for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done 

Modifiez la commande sed selon vos besoins, une fois que vous êtes prêt à apporter des modifications:

 for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed -i '' 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done 

Notez le -i '' dans la commande sed , je ne voulais pas créer une sauvegarde des fichiers originaux (comme expliqué dans Modifications sur place avec sed sur OS X ou dans le commentaire de Robert Lujo sur cette page).

Heureux les gens qui sèment!

 perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *` 

pour modifier plusieurs fichiers (et enregistrer une sauvegarde en tant que *.bak ):

 perl -p -i -e "s/\|/x/g" * 

va prendre tous les fichiers dans le répertoire et remplacer | avec x appelé une “tarte Perl” (facile comme une tarte)

J’utilise juste des tops:

 find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 | xargs -0 tops -verbose replace "verify_noerr()" with "__Verify_noErr()" \ replace "check()" with "__Check()" 

Une manière plus simple consiste à utiliser le ci-dessous sur la ligne de commande

 find /home/www/ -type f|xargs perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g' 

C’est la meilleure solution globale que j’ai trouvée pour OSX et Windows (msys2). Devrait fonctionner avec tout ce qui peut obtenir la version GNU de sed. Ignore les répertoires .git pour ne pas corrompre vos sums de contrôle.

Sur mac, installez d’abord coreutils et assurez-vous que gsed est dans le chemin –

 brew install coreutils 

Ensuite, je colle cette fonction dans mon zshrc / bashrc ->

 replace-recursive() { hash gsed 2>/dev/null && local SED_CMD="gsed" || SED_CMD="sed" find . -type f -name "*.*" -not -path "*/.git/*" -print0 | xargs -0 $SED_CMD -i "s/$1/$2/g" } usage: replace-recursive   

Pour remplacer tout le contenu correspondant à chaîne_1 par chaîne_2 de tous les fichiers .c et .h dans le répertoire et les sous-répertoires en cours (à l’exception de .git /) .

Cela fonctionne sur Mac :

 find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \ sed -i '' -e 's/'$1'/'$2'/g' {} + 

Cela devrait fonctionner sous Linux (pas encore testé):

 find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \ sed -i 's/ssortingng_1/ssortingng_2/g' {} + 

Si vous avez access au nœud, vous pouvez faire un npm install -g rexreplace , puis

 rexreplace 'subdomainA.example.com' 'subdomainB.example.com' /home/www/**/*.*