Substitua várias strings por outra string em vários arquivos

6

Eu tenho vários arquivos de texto. Todos esses arquivos de texto precisam passar por um conjunto para edição, o que pode ser feito em vim . Eu quero automatizar isso. vim tem vários comandos para substituição. Suponha que os arquivos de texto precisem passar pelas seguintes substituições:

  1. substitua boy por Boy: %s/boy/Boy/g
  2. substituir garota por Garota: %s/girl/Girl/g
  3. excluir linhas vazias: g/^$/d

Este é apenas um exemplo simples, existe uma maneira de escrever todas essas regras e depois automatizar isso em vários arquivos?

    
por Noor 17.09.2014 / 13:16

9 respostas

12

Sim, existe uma maneira de automatizar isso. E começa com a seleção da ferramenta certa para o trabalho.
Nesse caso, você deve usar, por exemplo, sed e não tentar dobrar vi , que foi projetado para uso interativo (e não para automação).

A sintaxe de substituição para sed é basicamente igual à de vi .

 sed -i.backup 's/boy/Boy/g' file-name-1 file-name-2 ...
    
por 17.09.2014 / 13:22
10

Os scripts do Vim (gvim, vim) podem ser elegantes e muito fáceis de adaptar

vi -s edit.vim  test.txt

onde edit.vim contém (o: wq é opcional)

:%s/boy/Boy/g
:%s/girl/Girl/g
:g/^$/d
:wq

onde test.txt contém

boys & girls

boys & girls
boy & girl

boys & girls

aqui está um script vim genérico para limpar um arquivo de texto "mucky"

:" clean.vim
:" clean up a text file
:" delete DOS returns (actually superfluous because of non-ASCII
deletion)
:%s/\r//eg
:" delete any non-ASCII including invisibles
:%s/[\x00-\x1f\x80-\xff]/ /eg
:" compress multiple spaces
:%s/\s\s\+/ /eg
:" delete end of line spaces
:%s/\s\+$//e
:" compress multiple blank lines
:v/./,/./-j
:" sort eliminating duplicate lines
:%sort -u

lembre-se que você também pode criar o script a partir do vim

:source clean.vim
    
por 17.09.2014 / 14:34
3

Por que não usar sed ?

Com o sed você pode passar facilmente pelos arquivos no diretório:

for f in *.txt
do
sed 's/boy/Boy/g;s/girl/Girl/g;/^\s*$/d' $f > tmp
mv tmp $f
done

O exemplo acima mudaria de menino- > Menino, menina > Menina e excluirá as linhas vazias.

    
por 17.09.2014 / 13:45
2

E um script vi in-line:

$ vi test.txt -c '%s/boy/Boy/g | %s/girl/Girl/g | g/^$/d | wq'
    
por 17.09.2014 / 14:57
2

OBSERVAÇÃO: usar sed é a maneira apropriada de fazer isso, conforme mostrado por @ Resposta de Anthon !

Só para mostrar como você pode fazer isso em vim . Você pode usar o comando argdo em vim . Do :help argdo em vim :

:argdo[!] {cmd}         Execute {cmd} for each file in the argument list.
Exemplo:
    :args *.c
    :argdo set ff=unix | update

Isso define a opção 'fileformat' como "unix" e grava o arquivo se estiver agora mudou. Isso é feito para todos os arquivos *.c .

Exemplo:
    :args *.[ch]
    :argdo %s/\<my_foo\>/My_Foo/ge | update

Isso altera a palavra "my_foo" para "My_Foo" em todos os arquivos *.c e *.h . O sinalizador "e" é usado para o comando :substitute para evitar um erro nos arquivos em que "my_foo" não é usado. :update grava o arquivo somente se alterações foram feitas.

O comando :args <pattern> informa a :argdo quais arquivos você deseja executar o seguinte {cmd} contra. Uma vez definida, você executa :argdo {cmd} | update , em que {cmd} pode ser sua substituição s/boy/Boy/g .

Os arquivos estão abertos durante esta operação?

Parece que sim. Eu fiz o seguinte teste para confirmar isso.

$ for i in {1..3};do echo "dog" > file${i}.txt;done

$ head file*.txt
==> file1.txt <==
dog

==> file2.txt <==
dog

==> file3.txt <==
dog

Agora, vá para vim e faça os seguintes comandos:

:args file*.txt

Você pode confirmar que todos foram abertos:

:ls
  2 %a   "file1.txt"                    line 1
  3      "file2.txt"                    line 0
  4      "file3.txt"                    line 0

Fazer :argdo ... :

:argdo %s/dog/cat/g | update
"file1.txt" 1L, 4C written
"file2.txt" 1L, 4C written
"file3.txt" 1L, 4C written

Portanto, tenha isso em mente se você pretende usar essa abordagem. Ele abre ineficientemente todos os arquivos que correspondem ao seu padrão para :args e aplica o comando a eles por vez.

    
por 17.09.2014 / 13:55
0

sed é a ferramenta para você. A maioria dos comandos que você usará em vi estará disponível em sed e, portanto, não há muita curva de aprendizado. Os comandos em vi que são iniciados com : são baseados no editor subjacente ed e também estão disponíveis em sed . Você usará esses comandos sem o : e eles serão, por padrão, aplicados ao arquivo inteiro. Por exemplo, para o seu exemplo, você fará

sed -i 's/boy/Boy/g
s/girl/Girl/g
/^$/d' file1 file2 ...
    
por 17.09.2014 / 13:44
0

Supondo que seu vi seja realmente vim ou gvim , ele certamente pode ser implementado no vimscript, mas pode ser complicado trabalhar em vários arquivos de uma só vez.

Melhore a ferramenta certa para o trabalho: sed , o editor de fluxo, que usou uma sintaxe muito semelhante a vi , mas destina-se a trabalhar em arquivos como um fluxo linha por linha, sem interação.

Com a opção -i , sed pode fazer alterações em vários arquivos. -i.backup moves an original file foo.txt to foo.txt.backup , and applies changes in place to foo.txt ':

sed -i.backup -e 's/boy/Boy/g' -e 's/girl/Girl/g' -e '/^$/d' f1.txt f2.txt ...

Você pode executar isso dentro de vi também com :! :

:!sed -i.backup -e 's/boy/Boy/g' -e 's/girl/Girl/g' -e '/^$/d' f1.txt f2.txt ...
    
por 17.09.2014 / 13:45
0

Eu a segunda resposta @ Anthon . Use sed :

$ sed -e "s/boy/Boy/g" -e "s/girl/Girl/g" -e '/^\s*$/d' file

NOTA: (Isso também exclui linhas com apenas espaços em branco)

Adicione -i a sed se você quiser substituir no local (seus arquivos estão sob controle de versão, certo?). Para vários arquivos:

$ for $f in $(ls mydir); do
   sed -e "s/boy/Boy/g" -e "s/girl/Girl/g" -e '/^\s*$/d' $f
done
    
por 17.09.2014 / 13:40
0

Alternativas

A menos que você realmente precise de recursos especiais do Vim, provavelmente é melhor usar ferramentas não interativas como sed , awk ou Perl / Python / Ruby / seu favorito linguagem de script aqui .

Dito isso, você pode usar o Vim não interativamente:

Modo em lote silencioso

Para um processamento de texto muito simples (ou seja, usando o Vim como um 'sed' ou 'awk' aprimorado, talvez apenas aproveitando as expressões regulares aprimoradas em um comando :substitute ), use Ex-mode .

REM Windows
call vim -N -u NONE -n -es -S "commands.ex" "filespec"

Nota: o modo de lote silencioso ( :help -s-ex ) atrapalha o console do Windows, portanto, talvez seja necessário fazer um cls para limpar após a execução do Vim.

# Unix
vim -T dumb --noplugin -n -es -S "commands.ex" "filespec"

Atenção: o Vim irá aguardar pela entrada se o arquivo "commands.ex" não existir; melhor verificar de antemão para a sua existência! Alternativamente, o Vim pode ler os comandos do stdin. Você também pode preencher um novo buffer com o texto lido de stdin e ler os comandos do stderr se você usar o argumento - .

Automação completa

Para um processamento mais avançado envolvendo várias janelas e a automação real do Vim (onde você pode interagir com o usuário ou deixar o Vim em execução para permitir que o usuário assuma o controle), use:

vim -N -u NONE -n -c "set nomore" -S "commands.vim" "filespec"

Aqui está um resumo dos argumentos usados:

-T dumb           Avoids errors in case the terminal detection goes wrong.
-N -u NONE        Do not load vimrc and plugins, alternatively:
--noplugin        Do not load plugins.
-n                No swapfile.
-es               Ex mode + silent batch mode -s-ex
                Attention: Must be given in that order!
-S ...            Source script.
-c 'set nomore'   Suppress the more-prompt when the screen is filled
                with messages or output to avoid blocking.
    
por 17.09.2014 / 20:48

Tags