Usando o Emacs para localizar e substituir recursivamente em arquivos de texto ainda não abertos


212

Como acompanhamento dessa pergunta , ele está tentando descobrir como fazer algo assim que deve ser fácil, o que me impede de me acostumar a usar o Emacs e, em vez disso, iniciar o editor com o qual já estou familiarizado. Eu uso o exemplo aqui com bastante frequência na edição de vários arquivos.

No Ultraedit, eu faria Alt + s e p para exibir uma caixa de diálogo com as opções: Localizar (inclui o uso de expressões regulares em várias linhas), Substituir por, Em Arquivos / Tipos, Diretório, Caixa de correspondência, Corresponder apenas a palavra inteira, Lista Arquivos alterados e subdiretórios de pesquisa. Normalmente, primeiro uso o mouse para clicar e arrastar e selecionar o texto que quero substituir.

Usando apenas em si Emacs (no Windows XP), sem chamar qualquer utilitário externo, como substituir todos foo \ NBAR com bar \ nbaz em *.ce *.harquivos em alguma pasta e todas as pastas abaixo dela. Talvez o Emacs não seja a melhor ferramenta para fazer isso, mas como isso pode ser feito facilmente com um comando mínimo?

Respostas:


370
  1. M-x find-name-dired: você será solicitado a fornecer um diretório raiz e um padrão de nome de arquivo.
  2. Pressione tpara "marcar / alternar" para todos os arquivos encontrados.
  3. Pressione Q"Query-Replace in Files ...": você será solicitado a regexps de consulta / substituição.
  4. Prossiga como query-replace-regexp: SPACEpara substituir e avançar para a próxima partida, npara pular uma partida, etc.
  5. Pressione C-x spara salvar os buffers. (Você pode pressionar y, nou !para salvar uma só vez)

10
Não, é recursivo. A versão não recursiva seria% m no modo direcionado.
22430 Chris Conway

5
Talvez porque você esteja chamando C: \ WINDOWS \ system32 \ find.exe, não o GNU find.
Jazz

6
Steen, Chris: Provavelmente não exatamente o que você deseja, mas você pode usar 'Cx s' (salvar alguns buffers); ele solicitará cada arquivo modificado, então você só precisará pressionar 'y' várias vezes.
21139 danielpoe

48
C-x s !para salvar todos os arquivos de uma vez.
CYrus

10
M-,para continuar pesquisando e substituindo (por exemplo, após o prompt "reverter arquivo", se o arquivo foi alterado no disco).
Tfischbach 11/09/12

37
  • M-x find-name-dired RET
    • pode demorar algum tempo até que todos os arquivos apareçam na lista, role para baixo ( M->) até que " find finished" apareça para garantir que todos tenham sido carregados
  • Pressione tpara "marcar / alternar" para todos os arquivos encontrados
  • Pressione Q"Query-Replace in Files ...": você será solicitado a regexps de consulta / substituição.
  • Prossiga como query-replace-regexp: SPACE ou ypara substituir e passar para a próxima correspondência, n para ignorar uma correspondência, etc.
    • Tipo ! para substituir todas as ocorrências no arquivo atual sem perguntar, Npara pular toda a substituição possível para o restante do arquivo atual. ( Né emacs 23+ apenas)
    • Para fazer a substituição em todos os arquivos sem perguntar mais, digite Y.
  • Chame “ibuffer” ( C-x C-bse estiver associado a ibuffer, ou M-x ibuffer RET) para listar todos os arquivos abertos.
  • Digite * upara marcar todos os arquivos não salvos, digite Spara salvar todos os arquivos marcados
  • * * RETpara desmarcar todas as marcas ou digite Dpara fechar todos os arquivos marcados

Esta resposta é combinada a partir desta resposta , deste site e de minhas próprias anotações. Usando o Emacs 23+.


3
ibuffernão está vinculado C-x C-bpor padrão.
Ph5 # php #

2
Bem, pessoalmente, eu recomendo que as pessoas não ligam C-x C-bpara ibuffer, porque é muito melhor do que a ligação padrão. Basta adicionar (global-set-key (kbd "C-x C-b") 'ibuffer)ao seu arquivo .emacs. Caso contrário, use M-x ibuffer RETas instruções acima.
Phillip # php #

Está bem. Acho que tenho as emacs arranque kit, que é por isso que o meu é obrigado
Frank Henard

1
O arquivo bla-bla-blaé visitado como somente leitura e para a pesquisa. Como evitar / ignorar isso e continuar substituindo? Obrigado.
Felipe

3
Por que ibufferquando você pode C-x s( save-some-buffers) e digita !? Eu não quero ser divertido, apenas curioso. PS vantagem para descrever !, Ne Y.
precisa saber é o seguinte

17

As respostas fornecidas são ótimas, no entanto, pensei em adicionar uma abordagem um pouco diferente.

É um método mais interativo, e requer wgrep, rgrepe iedit. Ambos iedite wgrepdevem ser instalados via MELPA ou Marmalade (usando M-x package-list-packages)

Primeira corrida M-x rgreppara encontrar a string que você está procurando.

Você poderá especificar tipos de arquivo / padrão e a pasta a ser recursiva.

Em seguida, você precisará executar wgrepiniciá-lo C-s C-p.

Wgrep vai deixar você editar os rgrepresultados, para definir uma região na corda para combinar e começar iedit-modecom C-;(dependendo do seu terminal, você pode precisar de re-ligamento isso)

Todas as ocorrências serão editáveis ​​de uma só vez. C-x C-scomprometer wgrep. Em seguida, C-x s !salve os arquivos alterados.

O principal benefício desse método é que você pode usar iedit-modepara desativar determinadas correspondências M-;. Você também pode usar os resultados rgreppara acessar os arquivos, por exemplo, se houver uma correspondência inesperada.

Acho muito útil fazer edições de origem e renomear símbolos (variáveis, nomes de funções etc.) em um projeto.

Se você ainda não conhece / usa o modo iedit, é uma ferramenta muito útil, recomendo fortemente que você dê uma olhada.


Essa técnica é bastante poderosa, mesmo sem usar o iedit. Acontece saber como grep (fácil com lgrep / rgrep) em saber como substituir e substituir a pesquisa em massa!
NeilenMarais

Combinar rgrep / wgrep / macros é incrível.
Ocodo 25/02

2
A combinação de teclas para iniciar wgrepé C-c C-pbtw.
techniao

15

Projétil é muito bom: Cc pr executa o comando projectile-replace


13

Geralmente, uso outras ferramentas para executar esta tarefa, e parece que muitas das abordagens mencionadas no shell de entrada Localizar e Substituir Across de Arquivos do EmacsWiki , mas o pacote Findr parece muito promissor.

Roubando parte do arquivo de origem :

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.

como tento encontrar a string "Collectible" em todos os arquivos com extensão java e hxx usando findr.el?
swdev

@swdev: Mx findr-query-substituir RET Collectible RET <a substituição> RET * \ java RET <o diretório para iniciar a pesquisa em> RET..
ShreevatsaR

Eu gosto dessa abordagem (evita todas as alternâncias envolvidas na abordagem direta). Eu uso algumas funções de conveniência para combinar isso com o projétil (project-root-discover) e substituir a coisa no ponto: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ

6

Fonte de informação: 1

Para usuários do emacs pro:

  1. Chame dired para listar arquivos em dir ou chame find-dired se você precisar de todos os subdiretórios.
  2. Marque os arquivos que você deseja. Você pode marcar por regex digitando 【% m】.
  3. Digite Q para chamar dired-do-query-replace-regexp.
  4. Digite seu regex de localização e substitua a string. ☛ pattern padrão de regex elisp comum〕
  5. Para cada ocorrência, digite y para substituir, n para pular. Digite trl Ctrl + g】 para abortar toda a operação.
  6. Tipo ! para substituir todas as ocorrências no arquivo atual sem solicitar, N para pular toda a substituição possível pelo restante do arquivo atual. (N é emacs apenas 23)
  7. Para fazer a substituição em todos os arquivos sem perguntar mais, digite Y. (somente no Emacs 23)
  8. Ligue para o ibuffer para listar todos os arquivos abertos. Digite 【* u】 para marcar todos os arquivos não salvos, digite S para salvar todos os arquivos marcados, digite D para fechá-los todos.

Guia passo a passo para iniciantes do Emacs

Selecionar arquivos de destino

Inicie o emacs digitando “emacs” no prompt da interface da linha de comandos. (Ou clique duas vezes no ícone Emacs se você estiver em um ambiente de interface gráfica do usuário)

Selecionando arquivos em um diretório

Primeiro, você precisa selecionar os arquivos que deseja fazer a substituição. Use o menu gráfico 〖Arquivo ▸ Abrir diretório〗. O Emacs solicitará um caminho de diretório. Digite o caminho do diretório e pressione Enter.

Agora, você verá a lista de arquivos e agora precisa marcar os arquivos nos quais deseja que a regex encontre / substitua para trabalhar. Você marca um arquivo movendo o cursor para o arquivo desejado e pressione m. Desmarque-o pressionando u. (Para listar subdiretórios, mova o cursor para o diretório e pressione i. O conteúdo do subdiretório será listado na parte inferior.) Para marcar todos os arquivos por uma regex, digite 【% m】 e digite seu padrão de regex. Por exemplo, se você quiser marcar todos os arquivos HTML, digite 【% m】 e .html $. (Você pode encontrar uma lista dos comandos de marcação no menu gráfico “Marca” (este menu aparece quando você está no modo direcionado).)

Selecionando arquivos em um diretório e todos os seus subdiretórios

Se você deseja localizar / substituir arquivos em um diretório, incluindo centenas de subdiretórios, aqui está um método para selecionar todos esses arquivos.

Ligue para a localização. (você chama um comando pressionando 【Alt + x】) Em seguida, digite um nome de diretório, ⁖ / Users / mary / myfiles

Nota: se você estiver usando o emacs em um terminal de texto não gráfico unix, e se 【Alt + x】 não funcionar, o toque equivalente da tecla será 【Esc x】.

O Emacs perguntará com o prompt “Run find (with args):”. Se você precisar fazer a substituição em todos os arquivos HTML, digite -name "* html". Se você não se importa com o tipo de arquivo, mas simplesmente com todos os arquivos nesse diretório, digite "-type f".

Agora, marque os arquivos como descrito acima.

Localizar / substituir interativo

Agora, você está pronto para fazer a busca interativa substituir. Por uma questão de simplicidade, digamos que você queira substituir a palavra "rápido" por "super". Agora, chame dired-do-query-replace-regexp. Ele solicitará a sequência regex e a sequência de substituição. Digite "rápido", digite e depois "super".

Agora, o emacs usará seu padrão e verificará os arquivos, parando e mostrando sempre que uma correspondência ocorrer. Quando isso acontece, o emacs solicitará a você, e você poderá fazer a alteração ou ignorá-la. Para fazer a alteração, digite y. Para pular, digite n. Se você simplesmente deseja que o emacs vá em frente e faça todas essas alterações no arquivo atual, digite!

Se você deseja cancelar toda a operação sem salvar as alterações feitas, digite 【Ctrl + g】 e saia do emacs usando o menu 〖Arquivo ▸ Sair do Emacs〗.

Salvando os arquivos alterados

Agora, depois de passar pela provação acima, há mais uma etapa que você precisa executar, que é salvar os arquivos alterados.

Se você estiver usando o emacs versão 22 ou posterior, chame ibuffer para entrar no modo de listagem de buffer, digite 【* u】 para marcar todos os arquivos não salvos e digite S para salvar todos eles. (isso é turnos)

Se você estiver usando uma versão 21 do emacs, poderá fazer o seguinte: buffers de lista de chamadas, mova o cursor para o arquivo que deseja salvar e digite s. Ele marcará o arquivo para uma ação posterior salva. Digite u para desmarcar. Quando terminar, digite x para executar o salvamento de todos os arquivos marcados para salvar. (no emacs, o arquivo aberto é chamado de "buffer". Desconsidere outras coisas lá.)

Alternativa às opções acima, você também pode chamar save-some-buffers (Ctrl + xs). Em seguida, o emacs exibirá cada arquivo não salvo e perguntará se você deseja que ele seja salvo.

Nota: o regex do emacs não é o mesmo que o Perl ou o Python, mas é semelhante. Para um resumo e padrões comuns, consulte: Emacs Regex.


2
Esta resposta deve torná-la mais amigável para iniciantes do que a resposta mais votada que espero. A propósito, obrigado por me motivar a colocar a coisa toda em vez do link.
Prashant Sharma

A resposta começa com a fonte: como a primeira linha. A justificativa para colar cópias é: às vezes, o link externo para blogs fica desatualizado e, em seguida, a resposta se torna inútil. E assim, como sugerido pela meta wiki. Eu decidi copiar a coisa toda.
Prashant Sharma

5

Usar o dired para recuar em uma árvore profunda de diretórios será um pouco lento para esta tarefa. Você pode considerar usar tags-query-replace . Isso significa criar uma tabela de tags, mas geralmente é útil de qualquer maneira, e é rápido.


Apenas tentei em um diretório com mais de 14.000 arquivos. Demorou alguns segundos para aparecer no Emacs. Então definitivamente não é lento (mais).
Berend de Boer

3

Para buffers abertos, é isso que eu faço:

(defun px-query-replace-in-open-buffers (arg1 arg2)
  "query-replace in all open files"
  (interactive "sRegexp:\nsReplace with:")
  (mapcar
   (lambda (x)
     (find-file x)
     (save-excursion
       (goto-char (point-min))
       (query-replace-regexp arg1 arg2)))
   (delq
    nil
    (mapcar
     (lambda (x)
       (buffer-file-name x))
     (buffer-list)))))

3

M-X Dired, et para marcar todos os arquivos e Qpara consultar substituir texto em todos eles. Você pode expandir um subdiretório usando o icomando antes da substituição da consulta. A principal informação que estou adicionando é que, se você der um prefixo (control-u) ao comando i, ele solicitará o argumento arg e o argumento -R expandirá recursivamente tudo subdirs no diredbuffer. Então agora você pode pesquisar todos os arquivos em um diretório inteiro.


2

Outra opção é usar pesquisa Icicles . Esse é um tipo diferente de pesquisa incremental que usa a conclusão da entrada do minibuffer contra ocorrências de pesquisa. Conforme você modifica sua entrada atual, o conjunto de ocorrências correspondentes é atualizado no buffer *Completions*.

Você pode pesquisar qualquer número de arquivos, buffers ou locais marcados, que você pode escolher usando a correspondência do padrão minibuffer (por exemplo, regexp).

Quando você visita um hit de pesquisa, pode substituir por demanda o hit inteiro ou apenas a parte dele que corresponde à entrada atual do minibuffer. A substituição sob demanda significa que você não é consultado sobre cada ocorrência de pesquisa por vez; você acessa os hits que deseja diretamente, em qualquer ordem. Essa abordagem pode ser mais eficaz do que substituir a consulta se você tiver um número limitado de substituições a fazer: ignore o exaustivoy/n solicitação .

A pesquisa termina nos contextos de pesquisa que você define - você não está limitado a pesquisar todo o texto nos arquivos de destino (por exemplo, você pode pular comentários ou tipos específicos de seções do programa). Um exemplo simples de um contexto de pesquisa é uma linha, como emgrep , mas um contexto pode ser qualquer bloco de texto correspondente ao padrão que você desejar. Normalmente, você define os contextos de pesquisa usando uma regexp, mas pode usar uma função. Além de definir os seus próprios, existem comandos predefinidos de busca de sincelos para diferentes tipos de contextos: blocos de propriedades de texto ou propriedades de sobreposição, coisas no momento, etc.

Você também pode classificar os resultados da pesquisa em várias ordens de classificação para facilitar o acesso / navegação.


2

find-name-dired está bem, mas:

  • Todos os arquivos obtidos correspondem à mesma regexp única.
  • find-diredé mais flexível a esse respeito, mas também é feito para o uso de regras gerais (mesmo que possam ser arbitrariamente complexas). E, claro, findtem uma linguagem complexa e própria.
  • se você quiser atuar apenas em alguns dos arquivos cujos nomes foram coletados no find(-name)-diredbuffer, será necessário marcá-los ou excluir / omitir as linhas daqueles em que você não deseja atuar.

Uma alternativa é usar comandos Dired + que atuam em (a) os arquivos marcados e (b) todos os arquivos marcados (ou todos os arquivos, se nenhum estiver marcado) nos subdiretórios marcados ... encontrados recursivamente . Isso oferece generalidade e controle fácil sobre a escolha do arquivo. Esses comandos "aqui e abaixo" estão todos na tecla de prefixo M-+no modo Dired.

Por exemplo, M-+ Qé o mesmo que Q--- query-replace, mas os arquivos de destino são todos aqueles marcados no diretório atual e em todos os subdiretórios marcados, baixo, baixo, baixo ...

Sim, uma alternativa ao uso desses comandos aqui e abaixo é inserir todos os subdiretórios e seus subdiretórios, recursivamente, e usar um comando de nível superior, como Q. Mas muitas vezes pode ser conveniente não se preocupar com os subdiretórios inseridos.

E para fazer isso, você precisa de uma maneira rápida de inserir todos esses subdiretórios recursivamente . Aqui também, Dired + pode ajudar. M-+ M-iinsere todos os subdiretórios marcados e seus próprios subdiretórios marcados, recursivamente. Ou seja, é como M-i(que insere os subdiretórios marcados no Dired + ), mas atua recursivamente nos subdiretórios.

(Todos esses comandos Dired + "aqui e abaixo" estão no menu Múltiplos > Marcados aqui e abaixo .)

Você também pode executar operações Dired em um conjunto de arquivos Emacs , que é um conjunto salvo de nomes de arquivos localizados em qualquer lugar. E se você usar Sincelos , poderá abrir um buffer Dired apenas para os arquivos em um conjunto de arquivos ou outros tipos de listas de arquivos salvos.

Você também pode marcar qualquer buffer direcionado, incluindo aquele criado usando find(-name)-dired. Isso fornece uma maneira rápida de retornar a esse conjunto (por exemplo, um conjunto de projetos) posteriormente. E se você usar o Bookmark + , os marcadores favoritos de um buffer direcionado (a) suas lsopções, (b) quais arquivos estão marcados, (c) quais subdiretórios são inseridos e (d) quais (sub) diretórios estão ocultos. Tudo isso é restaurado quando você "pula" para o marcador. Marcador + também permite marcar uma árvore inteira de buffers Dired --- saltar para o marcador restaura todos os buffers da árvore.


Quem quer que seja: Importe-se de explicar por que você rebaixou esta resposta?
Drew

1

Gostaria de sugerir mais uma ótima ferramenta que ainda não foi mencionada, a saber, Helm .

É um ótimo substituto para muitas operações padrão do Emacs que envolvem conclusão, pesquisa etc. Em particular, helm-find-files permite executar a substituição de consultas (incluindo regexp) em vários arquivos selecionados.

Basta abrir helm-find-files, marcar os arquivos relevantes M-SPCe, em seguida, use F6ou F7para executar a substituição da consulta ou regexp de substituição da consulta nos arquivos selecionados.


Pode helm-find-filesmarcar por regexp em vez de M-SPC?
Nordlöw

-2

Não é o Emacs, mas o xxdiff vem com uma ferramenta chamada xx-rename que faz isso para várias seqüências de caracteres de uma vez (por exemplo, de para de para de para), com instruções interativas, salve backups de todos os arquivos modificados e produza uma breve log de alterações feitas com o contexto. É isso que costumo usar quando faço renomeações grandes / globais.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.