¿Le pedimos confirmación antes de cada reemplazo?

37

¿Hay alguna manera de hacer que sed me solicite confirmación antes de cada reemplazo? Algo similar a 'c' cuando se usa reemplazar dentro de vim.

¿Sed hace esto en absoluto?

Yuvi
fuente
3
Sería técnicamente posible pero más un ejercicio intelectual que un esfuerzo útil. Consulte ¿Cómo hacer un reemplazo de texto en una jerarquía de carpetas grandes? que tiene soluciones Vim y Perl.
Gilles 'SO- deja de ser malvado'
Yo estoy usando vim (args y argdo) cada vez que necesito 'confirmación', pero me preguntaba si había una manera 'simple'
Yuvi
1
Va en contra del propósito básico de sed: automatizar la edición en una secuencia.
teppic
@teppic no es realmente porque todavía tendrías que buscar las instancias en archivos de texto, que sed puede hacer por ti. Creo que la pregunta tiene sentido, en caso de que haya agregado los archivos incorrectos a una lista y quiera ver qué archivo estaba editando
Kolob Canyon

Respuestas:

37

Hacerlo con sedprobablemente no sería posible ya que es un editor de flujo no interactivo. Ajustar sedun guión requeriría pensar demasiado. Es más fácil simplemente hacerlo con vim:

vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in

Como se mencionó en los comentarios a continuación, así es como se usaría en varios archivos que coinciden con un patrón de globalización de nombre de archivo en particular en el directorio actual:

for fname in file*.txt; do
    vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done

O, si primero desea asegurarse de que el archivo realmente contenga una línea que coincida primero con el patrón dado, antes de realizar la sustitución,

for fname in file*.txt; do
    grep -q 'PATTERN' "$fname" &&
    vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done

Los dos bucles de shell anteriores se modificaron en findcomandos que hacen lo mismo pero para todos los archivos con un nombre particular en algún lugar o debajo de algún top-dirdirectorio,

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' {} \;

O bien, utilizando los bucles de shell originales y findintroduciendo nombres de ruta en ellos:

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 {} +

En cualquier caso, no quieres hacer algo así for filename in $( grep -rl ... )desde

  1. requeriría que greptermine de ejecutarse incluso antes de comenzar la primera iteración del bucle, que es poco elegante , y
  2. los nombres de ruta devueltos por grepse dividirían en palabras en espacios en blanco, y estas palabras sufrirían un bloqueo de nombre de archivo (esto descalifica nombres de ruta que contienen espacios y caracteres especiales).

Relacionado:

Kusalananda
fuente
2
La ventaja sedes que puede operar en múltiples archivos, por ejemplo, sed -i 's/old/new/g' /path/to/*.txto algo similar.
user1717828
10
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
tcpaiva
@tecepe, conviértalo en una respuesta, gracias.
Nishant
1
@Nishant Sería una respuesta frágil ya que descalificaría cualquier archivo que contenga espacios en blanco en su nombre o ruta.
Kusalananda
7

Puede obtener esto haciendo lo siguiente:

:%s/OLD_TEXT/NEW_TEXT/gc

Específicamente, agregando cdespués del tercer delimitador.

Tenga en cuenta que la opción 'c' solo funciona en Vim; no podrás usarlo seden la línea de comando.

Kevin M
fuente
44
Para aclarar: esto solo funciona en Vim, no en la línea de comando regular sed.
Brendan
El OP está etiquetado con vim, por lo que esta respuesta es aplicable; sin embargo, claramente vim y sed tienen diferentes habilidades
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
}

Si alimenta esto con un solo argumento, searchandreplace "AAA"solo busca esa cadena en el directorio actual, pero si la alimenta con argumentos dobles, searchandreplace AAA BBBademás del anterior, también le pide que reemplace la primera con la segunda línea por línea "para cada línea.

LyXTeX
fuente