Rechercher - remplacer avec confirmation

Fermé
Fred - 13 mai 2019 à 01:11
zipe31 Messages postés 36402 Date d'inscription dimanche 7 novembre 2010 Statut Contributeur Dernière intervention 27 janvier 2021 - 14 mai 2019 à 21:17
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

3 réponses

UnGnU Messages postés 1158 Date d'inscription lundi 2 mai 2016 Statut Contributeur Dernière intervention 22 décembre 2020 157
13 mai 2019 à 08:24
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
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
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 211 Date d'inscription jeudi 13 décembre 2018 Statut Membre Dernière intervention 8 octobre 2022 13
13 mai 2019 à 19:53
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 Date d'inscription mardi 14 mai 2019 Statut Membre Dernière intervention 14 mai 2019
Modifié le 14 mai 2019 à 13:28
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 211 Date d'inscription jeudi 13 décembre 2018 Statut Membre Dernière intervention 8 octobre 2022 13 > boxR Messages postés 4 Date d'inscription mardi 14 mai 2019 Statut Membre Dernière intervention 14 mai 2019
14 mai 2019 à 13:38
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 Date d'inscription mardi 14 mai 2019 Statut Membre Dernière intervention 14 mai 2019
14 mai 2019 à 14:40
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 211 Date d'inscription jeudi 13 décembre 2018 Statut Membre Dernière intervention 8 octobre 2022 13 > boxR Messages postés 4 Date d'inscription mardi 14 mai 2019 Statut Membre Dernière intervention 14 mai 2019
Modifié le 14 mai 2019 à 14:48
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