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?
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
pourfind
si vous utilisez-exec <command>
ouxargs
(l'argument-0
est requis dansxargs
.)
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.
Le moyen le 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 <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.
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 , 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.
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.
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
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"
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/ )
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
.
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.
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 !!!
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.
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
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!
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
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'
Selon this blog:
find . -type f | xargs Perl -pi -e 's/oldtext/newtext/g;'
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>)"
juste pour éviter de changer aussi
mais reste
(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' {} \;
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)
Pour Qshell (qsh) sur IBMi, pas bash comme indiqué par OP.
Limitations des commandes qsh:
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:
Perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
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.
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'
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
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'
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/**/*.*
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>
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' {} +
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'