Rechercher - remplacer avec confirmation

Fred -  
zipe31 Messages postés 38797 Statut Contributeur -
Bonjour,

Je cherche à faire des "rechercher remplacer "récursivement dans des fichiers en affichant les fichiers modifiés ou en demandant confirmation avant remplacement pour éviter toute boulette...

Voici ce que j'ai trouvé :
- Afficher le motif à remplacer dans une recherche récursive
find -type f | xargs grep -l "motif_a_remplacer" -> mais j'ai des erreurs avec certains fichiers : "Aucun fichier ou dossier de ce type"

- Remplacer le motif
find -type f -exec sed -i 's/motif_a_remplacer/motif_de_remplacement/g' {} \;
A priori ça marche mais j'aimerais avant de faire le remplacement : soit en demander confirmation (Oui/non) soit en affichant juste la liste des fichiers qui seraient modifiés.


Une idée ?
Merci
Fred


Configuration: Windows / Firefox 66.0
A voir également:

3 réponses

UnGnU Messages postés 1468 Statut Contributeur 158
 
Salut,

Je n'ai pas testé en récursif, ni avec plusieurs remplacements dans le même fichier, mais l'idée est là, à toi d'adapter à ton cas :

Liste des fichiers :
$ tree
.
├── fich1
├── fich2
├── fich3
├── fich4
└── fich5

0 directories, 5 files


Contenu de chaque fichier :
$ more f*
::::::::::::::
fich1
::::::::::::::
motif
::::::::::::::
fich2
::::::::::::::
motif
::::::::::::::
fich3
::::::::::::::
motif
::::::::::::::
fich4
::::::::::::::
motif
::::::::::::::
fich5
::::::::::::::
motif


L'exécution de la ligne de commande avec demande :
$ find . -type f -exec bash -c 'grep -Hn "m" ${1};read -p "Remplacer ?" rep;if [ "${rep}" == o ];then sed -i "s/motif/AAA/" ${1};fi' _ {} \;
./fich5:1:motif
Remplacer ?o
./fich4:1:motif
Remplacer ?n
./fich3:1:motif
Remplacer ?n
./fich2:1:motif
Remplacer ?o
./fich1:1:motif
Remplacer ?n


Résultat avec remplacement sur fichier 2 et 5 :
$ more f*
::::::::::::::
fich1
::::::::::::::
motif
::::::::::::::
fich2
::::::::::::::
AAA
::::::::::::::
fich3
::::::::::::::
motif
::::::::::::::
fich4
::::::::::::::
motif
::::::::::::::
fich5
::::::::::::::
AAA

0
Fred
 
Merci de ton aide !
Bon après c'est un peu du chinois la syntaxe pour moi...
Quand il ne trouve pas le contenu "motif" dans le fichier, il pose tout de même la question ;((
Remplacer ?n
Remplacer ?n
Remplacer ?n
Remplacer ?n
Remplacer ?n
0
Fred
 
Voici ce que je vais finalement utiliser (merci Bruno !)

# sauvegarder la valeur du séparateur (pour la boucle for), en principe c'est un espace
IFS_BAK="$IFS"

# définir une autre valeur de séparateur : le retour à la ligne
IFS=$( echo -e "\n" )

# boucle sur tous les fichiers trouvés récursivement à partir du répertoire courant
for F in $( find ./ -type f )
do
# faire un test pour savoir si le fichier contient la chaine recherchée
TEST=$( grep 'monancienneadresse\.gmail' "$F" )

# si le test est positif
if [ "$TEST" != "" ]
then
# question
read -p "Remplacer dans $F ?" REPONSE
# si la réponse est o, faire le remplacement sur le fichier (attention, avec -i ça le fait!!!)
[ "$REPONSE" == "o" ] && sed -i 's/monancienneadresse\.gmail/manouvelle\.fr/g' "$F"
fi
done

# rétablir le séparateur par défaut
FS="$IFS_BAK"
0
lEprofSonDkon Messages postés 227 Statut Membre 13
 
il ne faut pas faire ça : si un nom de fichier peut contenir un espace, il peut aussi contenir un alinéa.
find ./ -type f -print0 | while IFS= read -d ''; do echo ">$REPLY<"; done

parce qu'un nom de fichier ne peut jamais contenir de caractère NULL.

et puis
if grep ...; then...; fi
0
boxR Messages postés 4 Statut Membre
 
On pourrait aussi bien l'écrire :
find ./ -type f | while read F; do echo "$F"; done

Sauf qu'avec ce type de solution, on ne peut pas poser de question à l'utilisateur dans la boucle, ça ne marche pas :
find ./ -type f | while read F; do echo "$F"; read -p "Votre réponse : " REPONSE; ... ; done

D'où l'idée du
for
ce qui nécessite la redéfinition de l'IFS.

Après, tu as déjà vu un saut de ligne dans un nom de fichier ?
0
lEprofSonDkon Messages postés 227 Statut Membre 13 > boxR Messages postés 4 Statut Membre
 
ben, oui. c'est pourquoi j'alerte.
et c'est pour ça qu'existe cette option (
-print0
), et son pendant dans d'autres programmes (
xargs -0
,
sort -z
...).

on peut créer des variables dans un pipe, il faut mettre tout ce qui suit la barre verticale entre accolades :
en shell POSIX :
commande | { while... var=blabla; done; echo "$var";}

ou en bash :
while... var="bibi"; done < <(commande)

en bash, dans un script, on peut conserver le pipe, et y créer des variables réutilisables en dehors, en positionnant l'option lastpipe avec shopt.
0
boxR Messages postés 4 Statut Membre
 
Admettons pour le retour à la ligne, même si ça me paraît exotique.

Le souci n'est pas de définir des variables localement, mais plutôt de faire un
read
dans la boucle.
Ça ne fonctionne pas, les deux
read
se télescoperont.
0
lEprofSonDkon Messages postés 227 Statut Membre 13 > boxR Messages postés 4 Statut Membre
 
commande | while IFS= read -d '' line; do read var </dev/tty; echo "$line -- $var"; done

« é voilà ! »

on pourrait aussi passer par des descripteurs de fichier...
0