¿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?
¿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?
Respuestas:
Hacerlo con sed
probablemente no sería posible ya que es un editor de flujo no interactivo. Ajustar sed
un 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 find
comandos que hacen lo mismo pero para todos los archivos con un nombre particular en algún lugar o debajo de algún top-dir
directorio,
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 find
introduciendo 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
grep
termine de ejecutarse incluso antes de comenzar la primera iteración del bucle, que es poco elegante , ygrep
se 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:
sed
es que puede operar en múltiples archivos, por ejemplo, sed -i 's/old/new/g' /path/to/*.txt
o algo similar.
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
Puede obtener esto haciendo lo siguiente:
:%s/OLD_TEXT/NEW_TEXT/gc
Específicamente, agregando c
después del tercer delimitador.
Tenga en cuenta que la opción 'c' solo funciona en Vim; no podrás usarlo sed
en la línea de comando.
sed
.
vim
, por lo que esta respuesta es aplicable; sin embargo, claramente vim y sed tienen diferentes habilidades
Puede dejar que sed
haga lo suyo en el archivo y luego guardar el resultado en un archivo temporal que luego puede parchear interactivamente en el archivo original utilizando sdiff
(consulte http://www.gnu.org/software/diffutils/manual/diffutils.html # Invocando-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
}
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 BBB
además del anterior, también le pide que reemplace la primera con la segunda línea por línea "para cada línea.