Fai sed chiedere conferma prima di ogni sostituzione?


37

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?


3
Sarebbe tecnicamente possibile ma più di un esercizio intellettuale che di uno sforzo utile. Vedi Come eseguire una sostituzione del testo in una gerarchia di cartelle grandi? che ha soluzioni Vim e Perl.
Gilles 'SO- smetti di essere malvagio' il

Io sto usando vim (args e argdo) ogni volta che ho bisogno di 'conferma', ma chiedevo se ci fosse un modo 'semplice'
Yuvi

1
Va contro lo scopo di base di sed - automatizzare l'editing su un flusso.
teppic

@teppic non proprio perché dovresti comunque trovare le istanze nei file di testo, cosa che sed può fare per te. Penso che la domanda abbia un senso, nel caso in cui tu abbia aggiunto i file sbagliati a un elenco e volessi vedere quale file stavi modificando
Kolob Canyon

Risposte:


37

Farlo con sedprobabilmente non sarebbe possibile in quanto si tratta di un editor di stream non interattivo. Avvolgere seduna 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 findcomandi che fanno le stesse cose ma per tutti i file con un nome particolare da qualche parte dentro o sotto una top-dirdirectory,

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 findinserendo 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

  1. richiederebbe che grepfinisca l'esecuzione prima ancora di iniziare la prima iterazione di loop, che è inelegante , e
  2. i grepnomi 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:


2
Il vantaggio sedè che può operare su più file, ad esempio sed -i 's/old/new/g' /path/to/*.txto qualcosa di simile.
user1717828,

10
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
tcpaiva,

@tecepe, ti preghiamo di convertirlo in una risposta, grazie.
Nishant,

1
@Nishant Sarebbe una risposta fragile in quanto squalificherebbe qualsiasi file contenente spazi bianchi nel suo nome o percorso.
Kusalananda

7

Puoi ottenerlo facendo questo:

:%s/OLD_TEXT/NEW_TEXT/gc

In particolare, aggiungendo il cdopo il terzo delimitatore.

Nota che l'opzione 'c' funziona solo in Vim; non sarai in grado di usarlo con seddalla riga di comando.


4
Per chiarire: questo funziona solo in Vim, non nella normale riga di comando sed.
Brendan,

L'OP è taggato con vim, quindi questa risposta è applicabile; tuttavia, chiaramente vim e sed hanno abilità diverse
ILMostro_7,


-1
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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.