Existe uma maneira de fazer com que o sed me peça uma confirmação antes de cada substituição? Algo semelhante ao 'c' ao usar substituir dentro do vim.
O sed faz isso?
Existe uma maneira de fazer com que o sed me peça uma confirmação antes de cada substituição? Algo semelhante ao 'c' ao usar substituir dentro do vim.
O sed faz isso?
Respostas:
Fazê-lo com sed
provavelmente não seria possível, pois ele é um editor de fluxo não-interativo. A quebra sed
de um script exigiria muito pensamento. É mais fácil fazer isso com vim
:
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in
Como foi mencionado nos comentários abaixo, veja como isso seria usado em vários arquivos que correspondam a um padrão de globbing de nome de arquivo específico no diretório atual:
for fname in file*.txt; do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Ou, se você deseja primeiro garantir que o arquivo realmente contenha uma linha que corresponda ao padrão especificado primeiro, antes de executar a substituição,
for fname in file*.txt; do
grep -q 'PATTERN' "$fname" &&
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Os dois loops de shell acima modificados em find
comandos que fazem as mesmas coisas, mas para todos os arquivos com um nome específico em algum lugar ou sob algum top-dir
diretório,
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' {} \;
Ou, usando os loops de shell originais e tendo find
nomes de caminho neles:
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 {} +
De qualquer forma, você não quer fazer algo como for filename in $( grep -rl ... )
desde
grep
execução fosse executada antes mesmo de iniciar a primeira iteração do loop, que é deselegante , egrep
seriam divididos em palavras em espaços em branco, e essas palavras sofreriam um globbing de nome de arquivo (isso desqualifica nomes de caminho que contêm espaços e caracteres especiais).Palavras-chave:
sed
é que ele pode operar em vários arquivos, por exemplo, sed -i 's/old/new/g' /path/to/*.txt
ou algo semelhante.
for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
Você pode obter isso fazendo o seguinte:
:%s/OLD_TEXT/NEW_TEXT/gc
Especificamente, adicionando o c
depois do terceiro delimitador.
Observe que a opção 'c' funciona apenas no Vim; você não poderá usá-lo sed
na linha de comando.
sed
.
vim
, portanto, esta resposta é aplicável; no entanto, claramente vim e sed têm habilidades diferentes
Você pode deixar o sed
seu trabalho no arquivo e salvar o resultado em um arquivo temporário, que pode ser conectado interativamente ao arquivo original usando sdiff
(consulte 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 você alimenta isso com argumento único - searchandreplace "AAA"
- ele procura apenas essa sequência no diretório atual, mas se você alimenta com argumentos duplos - searchandreplace AAA BBB
- além do acima, ele também solicita que você substitua a primeira pela segunda "linha por linha "para cada linha.