La commande suivante modifie correctement le contenu de 2 fichiers.
sed -i 's/abc/xyz/g' xaa1 xab1
Mais ce que je dois faire est de modifier plusieurs fichiers de manière dynamique et je ne connais pas les noms de fichiers. Je veux écrire une commande qui lira tous les fichiers du répertoire actuel en commençant par xa*
et sed
devrait changer le contenu du fichier.
Mieux encore:
for i in xa*; do
sed -i 's/asd/dfg/g' $i
done
car personne ne sait combien de fichiers sont présents et il est facile de dépasser les limites de la ligne de commande.
Voici ce qui se passe quand il y a trop de fichiers:
# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#
Je suis surpris que personne n'ait mentionné l'argument -exec à trouver, qui est destiné à ce type de cas d'utilisation, bien qu'il lance un processus pour chaque nom de fichier correspondant:
find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;
Vous pouvez également utiliser xargs, qui invoquera moins de processus:
find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'
Ou plus simplement, utilisez +
exec variante au lieu de ;
dans find pour permettre à find de fournir plusieurs fichiers par appel de sous-processus:
find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +
Vous pouvez utiliser grep et sed ensemble. Cela vous permet de rechercher des sous-répertoires de manière récursive.
Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'
For grep:
-r recursively searches subdirectories
-l prints file names that contain matches
For sed:
-i extension (Note: An argument needs to be provided on OS X)
Ces commandes ne fonctionneront pas dans la valeur par défaut sed
fournie avec Mac OS X.
De man 1 sed
:
-i extension
Edit files in-place, saving backups with the specified
extension. If a zero-length extension is given, no backup
will be saved. It is not recommended to give a zero-length
extension when in-place editing files, as you risk corruption
or partial content in situations where disk space is exhausted, etc.
A tenté
sed -i '.bak' 's/old/new/g' logfile*
et
for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done
Les deux fonctionnent bien.
@PaulR a posté ceci en tant que commentaire, mais les gens devraient le voir comme une réponse (et cette réponse fonctionne mieux pour mes besoins):
sed -i 's/abc/xyz/g' xa*
Cela fonctionnera pour un nombre modéré de fichiers, probablement de l’ordre de dizaines, mais probablement pas de l’ordre de millions .
Une autre méthode plus polyvalente consiste à utiliser find
:
sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')
J'utilise find
pour une tâche similaire. C'est très simple: vous devez le passer comme argument pour sed
comme ceci:
sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`
Ainsi, vous n'avez pas à écrire de boucles complexes et il est facile de savoir quels fichiers vous allez modifier. Il vous suffit d'exécuter find
avant d'exécuter sed
.
Si vous êtes capable d'exécuter un script, voici ce que j'ai fait pour une situation similaire:
En utilisant un dictionnaire/hashMap (tableau associatif) et des variables pour la commande sed
, nous pouvons parcourir le tableau en boucle pour remplacer plusieurs chaînes. L'inclusion d'un caractère générique dans le name_pattern
permettra de remplacer in-situ dans les fichiers par un motif (cela pourrait ressembler à name_pattern='File*.txt'
) dans un répertoire spécifique (source_dir
) . Tous les changements sont écrits dans la logfile
dans le destin_dir
#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'
echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "
declare -A pairs=(
['WHAT1']='FOR1'
['OTHER_string_to replace']='string replaced'
)
for i in "${!pairs[@]}"; do
j=${pairs[$i]}
echo "[$i]=$j"
replace_what=$i
replace_for=$j
echo " "
echo "Replace: $replace_what for: $replace_for"
find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g"
find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done
echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile
Tout d'abord, le tableau de paires est déclaré, chaque paire est une chaîne de remplacement, puis WHAT1
sera remplacé pour FOR1
et OTHER_string_to replace
sera remplacé pour string replaced
dans le fichier File.txt
. Dans la boucle, le tableau est lu, le premier membre de la paire est récupéré sous la forme replace_what=$i
et le second sous la forme replace_for=$j
. La commande find
cherche dans le répertoire le nom du fichier (qui peut contenir un caractère générique) et la commande sed -i
remplace dans le même fichier ce qui avait été défini précédemment. Enfin, j'ai ajouté une variable grep
redirigée vers le fichier journal pour consigner les modifications apportées au (x) fichier (s).
Cela a fonctionné pour moi dans GNU Bash 4.3
sed 4.2.2
et est basé sur la réponse de VasyaNovikov pour Loop over tuples in bash .
tu peux faire
' xxxx ' text u recherche et le remplacera par ' yyyy '
grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'