C'è un modo per fare in modo che sed mi chieda conferma prima di ogni sostituzione? Qualcosa di simile a 'c' quando si usa sostituire inside vim.
Sed lo fa affatto?
C'è un modo per fare in modo che sed mi chieda conferma prima di ogni sostituzione? Qualcosa di simile a 'c' quando si usa sostituire inside vim.
Sed lo fa affatto?
Risposte:
Farlo con sed
probabilmente non sarebbe possibile in quanto si tratta di un editor di stream non interattivo. Avvolgere sed
una sceneggiatura richiederebbe troppi pensieri. È più semplice farlo con vim
:
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in
Dato che è stato menzionato nei commenti qui sotto, ecco come questo sarebbe usato su più file che corrispondono a un particolare modello globbing di nome file nella directory corrente:
for fname in file*.txt; do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Oppure, se vuoi prima assicurarti che il file contenga davvero una riga che corrisponda prima al modello dato, prima di eseguire la sostituzione,
for fname in file*.txt; do
grep -q 'PATTERN' "$fname" &&
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
I due precedenti loop di shell sono stati modificati in find
comandi che fanno le stesse cose ma per tutti i file con un nome particolare da qualche parte dentro o sotto una top-dir
directory,
find top-dir -type f -name 'file*.txt' \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
find top-dir -type f -name 'file*.txt' \
-exec grep -q 'PATTERN' {} \; \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
Oppure, usando i loop shell originali e find
inserendo in essi i percorsi dei feed:
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
grep -q "PATTERN" "$pathname" &&
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
In ogni caso, non vuoi fare qualcosa di simile da for filename in $( grep -rl ... )
allora
grep
finisca l'esecuzione prima ancora di iniziare la prima iterazione di loop, che è inelegante , egrep
nomi dei percorsi restituiti verrebbero suddivisi in parole negli spazi bianchi e queste parole subirebbero il globbing del nome del file (questo squalifica i percorsi che contengono spazi e caratteri speciali).Relazionato:
sed
è che può operare su più file, ad esempio sed -i 's/old/new/g' /path/to/*.txt
o qualcosa di simile.
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
Puoi ottenerlo facendo questo:
:%s/OLD_TEXT/NEW_TEXT/gc
In particolare, aggiungendo il c
dopo il terzo delimitatore.
Nota che l'opzione 'c' funziona solo in Vim; non sarai in grado di usarlo con sed
dalla riga di comando.
sed
.
vim
, quindi questa risposta è applicabile; tuttavia, chiaramente vim e sed hanno abilità diverse
Puoi lasciare che sed
faccia il suo lavoro sul file e quindi salvare il risultato in un file temporaneo che puoi quindi applicare in modo interattivo al file originale usando sdiff
(vedi http://www.gnu.org/software/diffutils/manual/diffutils.html # Invoking-sdiff ):
sed -r 's/something/something_else/g' my_file > tmp_file
sdiff -o my_file -s -d my_file tmp_file
searchandreplace () {
if [ -z $2 ] ; then
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
else
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
exp=$(realpath * | xargs grep -ins --directories=skip --color=never "$1" | cut -d':' -f1,2 | awk -F':' '{print $2 $1}' | sed -E "s|^[0-9]+|sed -i \'&s/|; s|//|/"$1"/"$2"/g\' /|")
IFS=$'\n'
for d in $exp
do
read -p "Exec $(echo -e -n "\033[1;31m$d\033[0m")? " -e REP
if [ "$REP" = y ]; then
echo `bash -c $d`;
fi
done
fi
}
Se lo nutri con un singolo argomento - searchandreplace "AAA"
cerca solo quella stringa nella directory corrente, ma se lo nutri con doppi argomenti - searchandreplace AAA BBB
- oltre a quanto sopra ti chiede anche di sostituire il primo con la seconda "riga di linea "per ogni linea.