GNU/Linux >> Tutoriels Linux >  >> Linux

Comment remplacer une chaîne dans un ou plusieurs fichiers ?

Remplacer des chaînes dans des fichiers en fonction de certains critères de recherche est une tâche très courante. Comment puis-je

  • remplacer la chaîne foo avec bar dans tous les fichiers du répertoire courant ?
  • faire la même chose récursivement pour les sous-répertoires ?
  • remplacer uniquement si le nom du fichier correspond à une autre chaîne ?
  • remplacer uniquement si la chaîne est trouvée dans un certain contexte ?
  • remplacer si la chaîne est sur un certain numéro de ligne ?
  • remplacer plusieurs chaînes par le même remplacement
  • remplacer plusieurs chaînes par des remplacements différents

Réponse acceptée :

1. Remplacement de toutes les occurrences d'une chaîne par une autre dans tous les fichiers du répertoire courant :

Ce sont pour les cas où vous savez que le répertoire ne contient que des fichiers normaux et que vous souhaitez traiter tous les fichiers non cachés. Si ce n'est pas le cas, utilisez les approches en 2.

Tous sed les solutions dans cette réponse supposent que GNU sed . Si vous utilisez FreeBSD ou macOS, remplacez -i avec -i '' . Notez également que l'utilisation du -i basculer avec n'importe quelle version de sed a certaines implications sur la sécurité du système de fichiers et est déconseillé dans tout script que vous envisagez de distribuer de quelque manière que ce soit.

  • Non récursif, fichiers de ce répertoire uniquement :

     sed -i -- 's/foo/bar/g' *
     perl -i -pe 's/foo/bar/g' ./* 
    

(le perl un échouera pour les noms de fichiers se terminant par | ou espace)).

  • Fichiers réguliers récursifs (y compris les fichiers cachés ) dans ce répertoire et tous les sous-répertoires

     find . -type f -exec sed -i 's/foo/bar/g' {} +
    

    Si vous utilisez zsh :

     sed -i -- 's/foo/bar/g' **/*(D.)
    

    (peut échouer si la liste est trop longue, voir zargs contourner).

    Bash ne peut pas vérifier directement les fichiers normaux, une boucle est nécessaire (les accolades évitent de définir les options globalement) :

     ( shopt -s globstar dotglob;
         for file in **; do
             if [[ -f $file ]] && [[ -w $file ]]; then
                 sed -i -- 's/foo/bar/g' "$file"
             fi
         done
     )
    

    Les fichiers sont sélectionnés lorsqu'il s'agit de fichiers réels (-f) et qu'ils sont accessibles en écriture (-w).

2. Ne remplacez que si le nom du fichier correspond à une autre chaîne / a une extension spécifique / est d'un certain type, etc :

  • Fichiers non récursifs de ce répertoire uniquement :

    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz    ## files ending in .baz
    
  • Fichiers réguliers et récursifs dans ce répertoire et tous les sous-répertoires

    find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +
    

    Si vous utilisez bash (les accolades évitent de définir les options globalement) :

    ( shopt -s globstar dotglob
        sed -i -- 's/foo/bar/g' **baz*
        sed -i -- 's/foo/bar/g' **.baz
    )
    

    Si vous utilisez zsh :

    sed -i -- 's/foo/bar/g' **/*baz*(D.)
    sed -i -- 's/foo/bar/g' **/*.baz(D.)
    

Le -- sert à indiquer sed qu'aucun drapeau ne sera plus donné dans la ligne de commande. Ceci est utile pour se protéger contre les noms de fichiers commençant par - .

  • Si un fichier est d'un certain type, par exemple exécutable (voir man find pour plus d'options):

    find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
    

zsh :

    sed -i -- 's/foo/bar/g' **/*(D*)

3. Remplacer uniquement si la chaîne est trouvée dans un certain contexte

  • Remplacez foo avec bar seulement s'il y a un baz plus tard sur la même ligne :

     sed -i 's/foo(.*baz)/bar1/' file
    

Dans sed , en utilisant ( ) enregistre tout ce qui est entre parenthèses et vous pouvez ensuite y accéder avec 1 . Il existe de nombreuses variantes de ce thème, pour en savoir plus sur ces expressions régulières, voir ici.

  • Remplacez foo avec bar seulement si foo se trouve sur la colonne 3d (champ) du fichier d'entrée (en supposant des champs séparés par des espaces) :

     gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file
    

(nécessite gawk 4.1.0 ou plus récent).

  • Pour un champ différent, utilisez simplement $NN est le numéro du champ d'intérêt. Pour un séparateur de champs différent (: dans cet exemple) utilisez :

     gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file
    

Une autre solution utilisant perl :

    perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 

REMARQUE :à la fois le awk et perl les solutions affecteront l'espacement dans le fichier (supprimez les blancs de début et de fin et convertissez les séquences de blancs en un caractère d'espacement dans les lignes qui correspondent). Pour un champ différent, utilisez $F[N-1]N est le numéro de champ que vous voulez et pour un séparateur de champ différent, utilisez (le $"=":" définit le séparateur de champ de sortie sur : ):

    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • Remplacez foo avec bar uniquement sur la 4ème ligne :

     sed -i '4s/foo/bar/g' file
     gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
     perl -i -pe 's/foo/bar/g if $.==4' file
    

4. Opérations de remplacement multiples :remplacer par des chaînes différentes

  • Vous pouvez combiner sed commandes :

     sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
    

Soyez conscient que l'ordre est important (sed 's/foo/bar/g; s/bar/baz/g' remplacera foo avec baz ).

  • ou commandes Perl

     perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
    
  • Si vous avez un grand nombre de motifs, il est plus facile d'enregistrer vos motifs et leurs remplacements dans un sed fichier de script :

     #! /usr/bin/sed -f
     s/foo/bar/g
     s/baz/zab/g
    
  • Ou, si vous avez trop de paires de modèles pour que ce qui précède soit réalisable, vous pouvez lire les paires de modèles à partir d'un fichier (deux modèles séparés par des espaces, $pattern et $replacement, par ligne) :

     while read -r pattern replacement; do   
         sed -i "s/$pattern/$replacement/" file
     done < patterns.txt
    
  • Ce sera assez lent pour les longues listes de modèles et les gros fichiers de données, vous voudrez peut-être lire les modèles et créer un sed script d'eux à la place. Ce qui suit suppose un <espace> le délimiteur sépare une liste de MATCH<espace>REPLACE paires apparaissant une par ligne dans le fichier patterns.txt :

     sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
     sed -f- ./editfile >outfile
    

Le format ci-dessus est largement arbitraire et, par exemple, ne permet pas un <espace> dans l'un des MATCH ou REPLACE . La méthode est cependant très générale :en gros, si vous pouvez créer un flux de sortie qui ressemble à un sed script, vous pouvez alors sourcer ce flux en tant que sed script en spécifiant sed le fichier de script sous la forme - stdin.

  • Vous pouvez combiner et concaténer plusieurs scripts de la même manière :

     SOME_PIPELINE |
     sed -e'#some expression script'  
         -f./script_file -f-          
         -e'#more inline expressions' 
     ./actual_edit_file >./outfile
    

Un sed POSIX concaténera tous les scripts en un seul dans l'ordre dans lequel ils apparaissent sur la ligne de commande. Aucun de ceux-ci n'a besoin de se terminer par un n ewline.

  • grep peut fonctionner de la même manière :

     sed -e'#generate a pattern list' <in |
     grep -f- ./grepped_file
    
  • Lorsque vous travaillez avec des chaînes fixes en tant que modèles, il est recommandé d'échapper les métacaractères des expressions régulières . Vous pouvez le faire assez facilement :

     sed 's/[]$&^*./[]/\&/g
          s| *([^ ]*) *([^ ]*).*|s/1/2/g|
     ' <patterns.txt |
     sed -f- ./editfile >outfile
    

5. Opérations de remplacement multiples :remplacez plusieurs modèles par la même chaîne

  • Remplacez l'un des foo , bar ou baz avec foobar

     sed -Ei 's/foo|bar|baz/foobar/g' file
    
  • ou

     perl -i -pe 's/foo|bar|baz/foobar/g' file
    

Linux
  1. Comment utiliser Sed pour remplacer une chaîne multiligne ?

  2. Comment passer une chaîne (pas un fichier) à Openssl ?

  3. Comment puis-je ajouter une chaîne au début de chaque fichier dans un dossier en bash ?

  4. Comment remplacer une chaîne dans plusieurs fichiers en ligne de commande Linux

  5. commande tr - comment remplacer la chaîne \n par une nouvelle ligne réelle (\n)

Comment utiliser sed pour rechercher et remplacer une chaîne dans des fichiers

Comment trouver une chaîne dans un fichier sous Linux

Remplacement de chaîne dans Bash

Comment utiliser la commande SED pour rechercher et remplacer une chaîne dans des fichiers

Comment rechercher et remplacer du texte, un mot ou une chaîne dans un fichier

Comment utiliser Sed pour rechercher et remplacer une chaîne dans un fichier