web-dev-qa-db-fra.com

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 de l'arborescence de répertoires /home/www/ de manière récursive?

618
Tedd
find /home/www \( -type d -name .git -Prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

De man find:

- print (GNU find uniquement) indique à find d'utiliser le caractère nul (\0) au lieu de l'espace comme délimiteur de sortie entre les chemins d'accès trouvés. Cette option est plus sûre si vos fichiers peuvent contenir des blancs ou d’autres caractères spéciaux. Il est recommandé d'utiliser l'argument -print0 pour find si vous utilisez -exec <command> ou xargs (l'argument -0 est requis dans xargs.)

795
Nikita Fedyashev

Note: N'exécutez pas cette commande sur un dossier incluant un référentiel git - toute modification de .git pourrait 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 ici, c’est plus simple que la plupart et utilise sed au lieu de Perl, comme le demandait la question initiale.

234
John Zwinck

Le moyen le plus simple pour moi est

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

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

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>: recherche 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'invocations 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.

57
I159
cd /home/www && find . -type f -print0 |
  xargs -0 Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
39
Employed Russian

Pour moi, la solution la plus simple à retenir est https://stackoverflow.com/a/2113224/565525 , c'est-à-dire:

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

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

NOTE: S'il y a trop de fichiers à traiter, vous aurez Argument list too long. Solution de contournement - utilisez la solution find -exec ou xargs décrite ci-dessus.

29
Robert Lujo

Pour toute personne utilisant chercheur d'argent (ag)

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

Puisque ag ignore les fichiers/dossiers git/hg/svn par défaut, son exécution est sûre dans un référentiel.

25
Jacob Wang

Pour réduire le nombre de fichiers récursivement sed, vous pouvez grep pour votre instance de chaîne:

grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

Si vous exécutez man grep, vous remarquerez que vous pouvez également définir un indicateur --exlude-dir="*.git" si vous souhaitez omettre la recherche dans les répertoires .git, en évitant les problèmes d'index git comme d'autres l'ont fait remarquer avec politesse.

Vous conduisant à:

grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
15
domdambrogia

Un bel oneliner en plus. Utiliser git grep.

git grep -lz 'subdomainA.example.com' | xargs -0 Perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
14
Jimmy Kane

Celui-ci est compatible avec les dépôts 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-strings-in-files-in-your-git-repository/ )

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

find /home/www/ -type f listera tous les fichiers de/home/www/(et ses sous-répertoires). L’indicateur "-exec" indique à find d’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 de créer une commande pour plusieurs noms de fichiers.

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

Ainsi, il est possible d'atteindre votre objectif (et de gérer les noms de fichiers contenant des espaces) sans utiliser xargs -0 ou -print0.

8
unutbu

J'avais juste besoin de cela et je n'étais pas satisfait de la rapidité des exemples disponibles. Alors 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 par un jeu d'enfant alors que d'autres prenaient tellement de temps que je ne pouvais pas attendre jusqu'à la fin.

7
Henno

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 cela vous aidera !!!

4
sarath kumar

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 importer quelque chose dans un fichier "en lecture", ce qui évite ces arguments vicieux -print0, tout en préservant les espaces dans les noms de fichiers.

Ajouter plus un echo avant que sed vous permette de voir quels fichiers vont changer avant de le faire.

4
MadMan2064

Essaye ça:

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
3
RikHic
#!/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
3
petrus4

Un peu vieille école mais cela fonctionnait sous OS X.

Il y a peu de tromperies:

• N'éditera que les fichiers portant l'extension .sls dans le répertoire actuel

. doit être échappé pour s'assurer que sed ne les évalue pas comme "n'importe quel caractère"

, est utilisé comme délimiteur sed à la place de l'habituel /

Notez également qu'il s'agit d'éditer un modèle Jinja pour passer un variable dans le chemin d'accès d'un import (mais c'est un sujet hors sujet).

Tout d'abord, vérifiez que votre commande sed fait ce que vous voulez (cela ne fera qu'imprimer les modifications dans 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 de sauvegarde des fichiers d'origine (comme expliqué dans Éditions en place avec sed sur OS X ou dans le commentaire de Robert Lujo sur cette page).

Heureux peuple qui sème!

2
Raphvanns

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

Voici le deal:

  • récursivement grep 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 'string' 'pathname' -Rl).

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

  • après cela, vous pourrez éditer/remplacer à volonté dans vim en suivant un schéma similaire à celui fourni sur le lien indiqué:

    • :bufdo %s#string#replacement#gc | update
2
mzcl-mn

Voici une version qui devrait être plus générale que la plupart; il ne nécessite pas find (en utilisant du à la place), par exemple. Il nécessite xargs, qui ne se trouve que dans certaines versions de Plan 9 (comme 9front).

 du -a | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

Si vous souhaitez ajouter des filtres tels que les extensions de fichier, utilisez grep:

 du -a | grep "\.scala$" | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
2
bbarker

Selon this blog:

find . -type f | xargs Perl -pi -e 's/oldtext/newtext/g;'
2
J.Hpour

Je viens d'utiliser des hauts:

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

juste pour éviter de changer aussi

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

mais reste

  • subdomainA.example.com.IsIt.good

(peut-être pas bon dans l'idée derrière la racine du domaine)

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

pour modifier plusieurs fichiers (et enregistrer une sauvegarde au format *.bak):

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

prendra tous les fichiers dans le répertoire et remplacera | par x appelé "tarte Perl" (simple comme bonjour)

1
Stenemo

Pour Qshell (qsh) sur IBMi, pas bash comme indiqué 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

Donc 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
  • Not Bash comme marqué par OP
1
Christoff Erasmus
Perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
1
Sheena

Pour remplacer toutes les occurrences dans un référentiel git, vous pouvez utiliser:

git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

Voir Liste des fichiers dans le dépôt git local? pour d'autres options permettant de répertorier tous les fichiers d'un référentiel. Les options -z indiquent à git de séparer les noms de fichiers avec un zéro octet, ce qui garantit que xargs (avec l'option -0) peut séparer les noms de fichiers, même s'ils contiennent des espaces ou non.

1
Perseids

Une méthode simple si vous avez besoin de répertoires à exclure (--exclude-dir=.svn) et que vous pouvez aussi avoir noms de fichier avec des espaces (en utilisant 0Byte avec grep -Z et xargs -0

grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'
1
inetphantom

Utilisation de grep et sed

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

Si vous voulez utiliser ceci sans détruire complètement votre référentiel SVN, vous pouvez dire à 'find' d'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'
1
Marcus Floyd

Si vous avez accès au noeud, vous pouvez faire un npm install -g rexreplace puis

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

C'est la meilleure solution complète 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 sommes de contrôle.

Sur mac, commencez par installer coreutils et assurez-vous que gsed est sur 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 <find> <replace>
0
cchamberlain

Pour remplacer tout le contenu correspondant string_1 par string_2 de tous les fichiers . C et . H du répertoire et des sous-répertoires en cours (à l'exclusion 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 (n’a pas encore été testé):

find . -type f -path "*.git*" -Prune -o -name '*\.[ch]' -exec \
sed -i 's/string_1/string_2/g' {} +
0
Klas. S

Un moyen plus simple consiste à utiliser ce qui suit sur la ligne de commande

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