J'ai besoin de remplacer une chaîne dans un grand nombre de fichiers d'un dossier, avec uniquement un accès ssh
au serveur. Comment puis-je faire ceci?
cd /path/to/your/folder
sed -i 's/foo/bar/g' *
Les occurrences de "foo" seront remplacées par "bar".
Semblable à la réponse de Kaspar mais avec le drapeau g pour remplacer toutes les occurrences sur une ligne.
find ./ -type f -exec sed -i 's/string1/string2/g' {} \;
Pour la casse globale:
find ./ -type f -exec sed -i 's/string1/string2/gI' {} \;
La réponse de @ kev est bonne, mais n'affecte que les fichiers du répertoire immédiat. L'exemple ci-dessous utilise grep pour rechercher des fichiers de manière récursive. Cela fonctionne pour moi à chaque fois.
grep -rli 'old-Word' * | xargs -i@ sed -i 's/old-Word/new-Word/g' @
Cela a fonctionné pour moi:
find ./ -type f -exec sed -i 's/string1/string2/' {} \;
Cependant, cela n’a pas été: sed -i 's/string1/string2/g' *
. Peut-être que "foo" n'était pas censé être string1 et que "bar" n'était pas string2.
Pour remplacer une chaîne dans plusieurs fichiers, vous pouvez utiliser:
grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'
Par exemple.
grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
Il existe déjà quelques réponses standard à cette question. En règle générale, vous pouvez utiliser find pour répertorier les fichiers de manière récursive, puis effectuer les opérations avec sed ou Perl.
Pour les utilisations les plus rapides, vous trouverez peut-être que la commande rpl est beaucoup plus facile à mémoriser. Voici remplacement (foo -> bar), de manière récursive sur tous les fichiers:
rpl -R foo bar .
Vous aurez probablement besoin de l'installer (apt-get install rpl
ou similaire).
Cependant, pour les travaux plus difficiles qui impliquent des expressions régulières et une substitution arrière, ou des noms renommés ainsi que des fonctions de recherche et remplacement, l'outil le plus général et le plus puissant que je connaisse est repren , un petit Python. script que j’ai écrit il ya quelque temps pour certaines tâches de renommage et de refactoring plus épineuses. Les raisons pour lesquelles vous pourriez le préférer sont:
Pour l'utiliser, pip install repren
. Consultez le README pour des exemples.
Pour remplacer un chemin dans les fichiers (en évitant les caractères d'échappement), vous pouvez utiliser la commande suivante:
sed -i 's@old_path@new_path@g'
Le signe @ signifie que tous les caractères spéciaux doivent être ignorés dans une chaîne suivante.
Si votre chaîne contient une barre oblique (/), vous pouvez changer le délimiteur en '+'.
find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +
Cette commande serait exécutée de manière récursive dans le répertoire en cours.
Les occurrences de première ligne de "foo" seront remplacées par "bar". Et vous pouvez utiliser la deuxième ligne pour vérifier.
grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c
Si vous avez une liste de fichiers que vous pouvez utiliser
replace "old_string" "new_string" -- file_name1 file_name2 file_name3
Si vous avez tous les fichiers que vous pouvez utiliser
replace "old_string" "new_string" -- *
Si vous avez une liste de fichiers avec extension, vous pouvez utiliser
replace "old_string" "new_string" -- *.extension
J'ai concocté ma propre solution avant de trouver cette question (et les réponses)… .. J'ai cherché différentes combinaisons de «remplacer», «plusieurs» et «xml», parce que c'était ma candidature, mais je n'ai pas trouvé celle-ci.
Mon problème: j'avais des fichiers XML printaniers avec des données pour les cas de test, contenant des objets complexes. Un refactor sur le code source Java a modifié beaucoup de classes et ne s’appliquait pas aux fichiers de données XML. Afin de sauvegarder les données des cas de test, je devais changer tous les noms de classe dans tous les fichiers xml, répartis sur plusieurs répertoires. Tout en sauvegardant des copies de sauvegarde des fichiers XML d'origine (bien que ce ne soit pas une nécessité, car le contrôle de version me sauverait ici).
Je recherchais une combinaison de find
+ sed
, car cela fonctionnait pour moi dans d’autres situations, mais pas avec plusieurs remplacements à la fois.
Puis j'ai trouvé demander une réponse d'ubuntu et cela m'a aidé à construire ma ligne de commande:
find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
Et cela a parfaitement fonctionné (bon, mon cas avait six remplaçants différents). Mais veuillez noter que tous les fichiers * .xml du répertoire courant seront touchés. À cause de cela, et si vous êtes responsable devant un système de contrôle de version, vous voudrez peut-être d'abord filtrer et ne transmettre qu'à sed
ceux qui ont réellement les chaînes que vous voulez; comme:
find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
"Vous pouvez également utiliser find et sed, mais je trouve que cette petite ligne de Perl fonctionne bien.
Perl -pi -w -e 's/search/replace/g;' *.php
"(Extrait de http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php )
Mes meilleurs résultats proviennent de l'utilisation de Perl et de grep (pour garantir que le fichier ait l'expression de recherche)
Perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )
grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @
script pour la commande multiedit
multiedit [-n PATTERN] OLDSTRING NEWSTRING
A partir de la réponse de Kaspar, j'ai créé un script bash pour accepter les arguments de ligne de commande et limiter éventuellement les noms de fichiers correspondant à un modèle. Enregistrez dans votre $ PATH et rendez-le exécutable, puis utilisez simplement la commande ci-dessus.
Voici le script:
#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n
[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"
# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then # if -n option is given:
# replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
# replace OLDSTRING with NEWSTRING recursively in all files
find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi
Vraiment boiteux, mais je ne pouvais obtenir aucune des commandes sed fonctionner sous OSX, alors j'ai fait cette chose stupide à la place:
:%s/foo/bar/g
:wn
^ - Copiez ces trois lignes dans mon presse-papiers (oui, incluez la nouvelle ligne), puis:
vi *
et maintenez enfoncée la commande-v jusqu'à ce qu'il soit indiqué qu'il ne reste aucun fichier.
Dumb ... hacky ... efficace ...
Pour maintenir mon nœud anglais personnel, j'ai écrit un script utilitaire qui permet de remplacer plusieurs paires d'ancienne/nouvelle chaîne, pour tous les fichiers d'un répertoire de manière récursive.
Les multiples paires d’anciennes/nouvelles chaînes sont gérées dans une carte de hachage.
Le répertoire peut être défini via la ligne de commande ou la variable d’environnement, la mappe est codée en dur dans le script, mais vous pouvez modifier le code à charger à partir d’un fichier, si nécessaire.
Il nécessite bash 4.2, en raison de certaines nouvelles fonctionnalités.
en_standardize.sh:
#! /bin/bash
# (need bash 4.2+,)
#
# Standardize phonetic symbol of English.
#
# format:
# en_standardize.sh [<dir>]
#
# params:
# * dir
# target dir, optional,
# if not specified then use environment variable "$node_dir_en",
# if both not provided, then will not execute,
# *
#
paramCount=$#
# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
echo -e "dir specified (in command):\n\t$1\n"
targetDir=$1
Elif [[ -v node_dir_en ]]; then # environable set,
echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
targetDir=$node_dir_en
else # environable not set,
echo "dir not specified, won't execute"
exit
fi
# check whether dir exists,
if [ -d $targetDir ]; then
cd $targetDir
else
echo -e "invalid dir location:\n\t$targetDir\n"
exit
fi
# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'
# do replace,
for key in "${!itemMap[@]}"; do
grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done
echo -e "\nDone."
exit
L'éditeur de flux modifie plusieurs fichiers «en place» lorsqu'il est appelé avec le commutateur -i
, qui prend un fichier de sauvegarde se terminant en argument. Alors
sed -i.bak 's/foo/bar/g' *
remplace foo
par bar
dans tous les fichiers de ce dossier, mais ne descend pas dans les sous-dossiers. Cela générera toutefois un nouveau fichier .bak
pour chaque fichier de votre répertoire . Pour ce faire, de manière récursive pour tous les fichiers de ce répertoire et de ses sous-répertoires, vous avez besoin d'un assistant, comme find
, pour parcourir l'arborescence.
find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *
find
vous permet de restreindre davantage les fichiers à modifier en spécifiant des arguments supplémentaires tels que find ./ -name '*.php' -or -name '*.html' -print0
, si nécessaire.
Remarque: GNU sed
ne nécessite pas de fin de fichier, sed -i 's/foo/bar/g' *
fonctionnera également; FreeBSD sed
demande une extension, mais laisse un espace entre les deux, donc sed -i .bak s/foo/bar/g *
fonctionne.
Si le fichier contient des barres obliques inverses (les chemins généralement), vous pouvez essayer quelque chose comme ceci:
sed -i -- 's,<path1>,<path2>,g' *
ex:
sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all Shell scripts available)
J'ai trouvé celui-ci dans un autre article (je ne me souviens plus lequel) et bien qu'il ne soit pas le plus élégant, il est simple et, en tant qu'utilisateur novice de Linux, il ne m'a posé aucun problème.
for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done
si vous avez des espaces ou d’autres caractères spéciaux, utilisez un \
for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
pour les chaînes dans les sous-répertoires, utilisez **
for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
Je donne un exemple pour corriger une erreur Shebang commune dans les sources Python.
Vous pouvez essayer l'approche grep/sed. En voici un qui fonctionne avec GNU sed et ne casse pas un dépôt git:
$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}
Ou vous pouvez utiliser greptile :)
$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .
Je viens de tester le premier script et le second devrait également fonctionner. Soyez prudent avec les caractères d'échappement, je pense qu'il devrait être plus facile d'utiliser greptile dans la plupart des cas. Bien sûr, vous pouvez faire beaucoup de choses intéressantes avec sed, et pour cela il peut être préférable de maîtriser l’utilisation de xargs.
Sur un MacBook Pro, j’ai utilisé les éléments suivants (inspirés de https://stackoverflow.com/a/19457213/6169225 ):
sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
-i ''
s'assurera que vous ne faites aucune sauvegarde.
-e
pour les expressions rationnelles modernes.
La commande ci-dessous peut être utilisée pour rechercher d’abord les fichiers et les remplacer.
trouver . | xargs grep chaîne de recherche | sed 's/chaîne de recherche/nouvelle chaîne/g'
par exemple trouver. | xargs grep abc | sed 's/abc/xyz/g'
Il existe un moyen plus simple d’utiliser un fichier script simple:
# Sudo chmod +x /bin/replace_string_files_present_dir
ouvrez le fichier dans gedit ou l’éditeur de votre choix, j’utilise gedit ici.
# Sudo gedit /bin/replace_string_files_present_dir
Puis dans l'éditeur collez le texte suivant dans le fichier
#!/bin/bash
replace "oldstring" "newstring" -- *
replace "oldstring1" "newstring2" -- *
#add as many lines of replace as there are your strings to be replaced for
#example here i have two sets of strings to replace which are oldstring and
#oldstring1 so I use two replace lines.
Enregistrez le fichier, fermez gedit, puis quittez votre terminal ou fermez-le, puis démarrez-le pour pouvoir charger le nouveau script que vous avez ajouté.
Accédez au répertoire dans lequel vous souhaitez modifier plusieurs fichiers. Puis lancez:
#replace_string_files_present_dir
Appuyez sur Entrée et ceci remplacera automatiquement les oldstring et oldstring1 dans tous les fichiers qui les contiennent avec les (newstring) et newstring1 respectivement.
Il ignorera tous les répertoires et fichiers qui ne contiennent pas les anciennes chaînes.
Cela pourrait aider à éliminer le travail fastidieux de la saisie au cas où vous auriez plusieurs répertoires avec des fichiers nécessitant un remplacement de chaîne. Tout ce que vous avez à faire est de naviguer dans chacun de ces répertoires, puis exécutez:
#replace_string_files_present_dir
Tout ce que vous avez à faire est de vous assurer que vous avez inclus ou ajouté toutes les chaînes de remplacement comme je vous l'ai montré ci-dessus:
replace "oldstring" "newstring" -- *
à la fin du fichier / bin/replace_string_files_present_dir.
Pour ajouter une nouvelle chaîne de remplacement, ouvrez le script que nous avons créé en tapant ce qui suit dans le terminal:
Sudo gedit /bin/replace_string_files_present_dir
Ne vous inquiétez pas du nombre de chaînes de remplacement que vous ajoutez, elles n'auront aucun effet si oldstring n'est pas trouvé.
Utiliser la commande ack serait beaucoup plus rapide, comme ceci:
ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'
Aussi, si vous avez un espace blanc dans le résultat de la recherche. Vous devez y échapper.
Si vous souhaitez rechercher la chaîne search
et la remplacer par replace
dans plusieurs fichiers, c’est ma formule testée au combat sur une ligne :
grep -RiIl 'search' | xargs sed -i 's/search/replace/g'
Explication rapide de grep:
-R
- recherche récursive-i
- insensible à la casse-I
- ignore les fichiers binaires (vous voulez du texte, non?)-l
- affiche une liste simple en sortie. Nécessaire pour les autres commandesLa sortie de grep est ensuite redirigée vers sed (via xargs), qui est utilisé pour remplacer le texte. Le drapeau -i
modifiera le fichier directement. Supprimez-le pour une sorte de "mode sec".