Eu estou usando vim (args e argdo) sempre que eu preciso 'confirmação', mas queria saber se havia uma maneira 'mais simples'
Yuvi
1
Isso vai contra o objetivo básico do sed - automatizar a edição em um fluxo.
Teppic # 1/15
@teppic não porque você ainda precisa encontrar as instâncias nos arquivos de texto, o que o sed pode fazer por você. Eu acho que a pergunta faz sentido, apenas no caso de você adicionou os arquivos errados para uma lista e queria ver o arquivo que você estava editando
Kolob Canyon
Respostas:
37
Fazê-lo com sedprovavelmente não seria possível, pois ele é um editor de fluxo não-interativo. A quebra sedde 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 findcomandos que fazem as mesmas coisas, mas para todos os arquivos com um nome específico em algum lugar ou sob algum top-dirdiretório,
find top-dir -type f -name 'file*.txt' \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
Ou, usando os loops de shell originais e tendo findnomes 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
exigiria que a grepexecução fosse executada antes mesmo de iniciar a primeira iteração do loop, que é deselegante , e
os nomes de caminho retornados por grepseriam 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).
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.
Respostas:
Fazê-lo com
sed
provavelmente não seria possível, pois ele é um editor de fluxo não-interativo. A quebrased
de um script exigiria muito pensamento. É mais fácil fazer isso comvim
: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:
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,
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 algumtop-dir
diretório,Ou, usando os loops de shell originais e tendo
find
nomes de caminho neles:De qualquer forma, você não quer fazer algo como
for filename in $( grep -rl ... )
desdegrep
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:
fonte
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:
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.fonte
sed
.vim
, portanto, esta resposta é aplicável; no entanto, claramente vim e sed têm habilidades diferentesVocê 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 usandosdiff
(consulte http://www.gnu.org/software/diffutils/manual/diffutils.html # Invoking-sdiff ):fonte
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.fonte