web-dev-qa-db-fra.com

Comment remplacer une chaîne dans plusieurs fichiers en ligne de commande linux

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?

317
mridul4c
cd /path/to/your/folder
sed -i 's/foo/bar/g' *

Les occurrences de "foo" seront remplacées par "bar".

448
kev

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' {} \;
184
Céline Aussourd

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' @
110
pymarco

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.

25
Kaspar L. Palgi

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'

Blog source

23
Djacomo

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:

  • Prend en charge le renommage de fichiers ainsi que la recherche et le remplacement du contenu du fichier.
  • Consultez les modifications avant de vous engager à effectuer la recherche et le remplacement.
  • Prend en charge les expressions régulières avec substitution arrière, mots entiers, respect de la casse et conservation de la casse (remplacer les modes foo -> bar, Foo -> Bar, FOO -> BAR).
  • Fonctionne avec plusieurs remplacements, y compris des échanges (foo -> bar et bar -> foo) ou des ensembles de remplacements non uniques (foo -> bar, f -> x).

Pour l'utiliser, pip install repren. Consultez le README pour des exemples.

23
jlevy

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.

17
Ilya Suzdalnitski

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.

11
gopiariv

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
10
jiasir

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
8
Pawel Dubiel

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' {} \;
4
manuelvigarcia

"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
  • -e signifie exécuter la ligne de code suivante.
  • -i signifie éditer sur place
  • -w écrire des avertissements
  • -p boucle

"(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' @
3
Dennis

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
2

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 ...

2
Justin

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
1
Eric Wang

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.

1
Anaphory

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)
1
Shaik Amanulla

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
0
Kyle Turck

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.

0
exa

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.

0
Marquistador

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'

0
Amit

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é.

0
briancollins081

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. 

0
Patoshi パトシ

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 commandes

La 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".

0
Ignorant