Usando o Emacs para localizar e replace recursivamente em arquivos de texto que ainda não estão abertos

Como um follow-up para esta questão , ele está tentando descobrir como fazer algo como isso, que deve ser fácil, que especialmente me impede de se acostumar a usar o Emacs e, em vez disso, iniciar o editor que eu já estou familiarizado. Eu uso o exemplo aqui com bastante frequência na edição de vários arquivos.

Em Ultraedit eu faria Alt + s, em seguida, p para exibir uma checkbox de diálogo com as opções: Find (inclui o uso de expressões regulares em várias linhas), Substitua por, em arquivos / tipos, diretório, Match Case, Match Whole Word Only, List Arquivos alterados e sub-diretórios de pesquisa. Normalmente, primeiro uso o mouse para clicar e arrastar e selecionar o texto que quero replace.

Usando apenas o próprio Emacs (no Windows XP), sem chamar nenhum utilitário externo, como replace todos os foo \ nbar com bar \ nbaz em arquivos *.c e *.h em alguma pasta e em 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?

  1. Mx find-name-dired : você será solicitado a fornecer um diretório raiz e um padrão de nome de arquivo.
  2. Pressione t para “alternar marca” para todos os arquivos encontrados.
  3. Pressione Q para “Query-Replace in Files …”: você será solicitado a solicitar consultas / substituições.
  4. Continue como com query-replace-regexp : SPACE para replace e passar para a próxima correspondência, n para pular uma correspondência, etc.
  5. Pressione Cx s para salvar buffers. (Você pode então pressionar y , n ou ! Para salvar tudo de uma vez)
  • Mx find-name-dired RET
    • Pode demorar algum tempo até que todos os ficheiros apareçam na lista, desloque-se para o fundo ( M-> ) até que apareça ” find finished ” para se certificar de que todos foram carregados.
  • Pressione t para “alternar marca” para todos os arquivos encontrados
  • Pressione Q para “Query-Replace in Files …”: você será solicitado a solicitar consultas / substituições.
  • Proceda como com query-replace-regexp: SPACE ou y para replace e passar para a próxima correspondência, n para pular uma correspondência, etc.
    • Tipo ! para replace todas as ocorrências no arquivo atual sem perguntar, N para pular todas as substituições possíveis para o resto do arquivo atual. ( N é apenas emacs 23+)
    • Para fazer a substituição em todos os arquivos sem mais perguntas, digite Y
  • Chame “ibuffer” ( Cx Cb se for obrigado a ibuffer, ou Mx ibuffer RET ) para listar todos os arquivos abertos.
  • Digite * u para marcar todos os arquivos não salvos, digite S para salvar todos os arquivos marcados
  • * * RET para desmarcar todas as marcas ou digite D para fechar todos os arquivos marcados

Esta resposta é combinada a partir desta resposta , deste site e das minhas próprias notas. Usando o Emacs 23+.

Eu geralmente uso outras ferramentas para executar essa tarefa, e parece que muitas das abordagens mencionadas no shell de input Localizar e Substituir Across Files 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. 

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

É um método mais interativo e requer wgrep , rgrep e iedit . O iedit e o wgrep devem ser instalados via MELPA ou Marmalade (usando Mx package-list-packages )

Primeiro execute Mx rgrep para encontrar a string que você está procurando.

Você poderá especificar tipos / padrões de arquivos e a pasta a ser recursada.

Em seguida, você precisará executar o wgrep iniciá-lo com o Cs Cp .

O Wgrep permitirá que você edite os resultados do rgrep , portanto, defina uma região na string para corresponder e inicie o iedit-mode com C-; (dependendo do seu terminal, você pode precisar reconectar isto)

Todas as ocorrências serão editáveis ​​de uma só vez. Cx Cs para cometer wgrep . Então Cx s ! para salvar os arquivos alterados.

O principal benefício deste método é que você pode usar o iedit-mode para desativar certas correspondências M-; . Você também pode usar os resultados no rgrep para rgrep os arquivos, por exemplo, se você tiver uma correspondência inesperada.

Acho muito útil fazer edições de código-fonte e renomear símbolos (variables, nomes de funções, etc.) em um projeto.

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

Usar dired para recorrer a uma tree de diretórios profunda será um pouco lento para essa tarefa. Você pode considerar o uso de tags-query-replace . Isso significa gastar muito para criar uma tabela de tags, mas isso geralmente é útil e é rápido.

Fonte de informação: 1

Para usuários do emacs pro:

  1. Chame o dired para listar os arquivos no dir, ou chame o find-dired se 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 achado regex e substitua string. El ☛ padrão regular regular elisp〕
  5. Para cada ocorrência, digite y para replace, n para pular. Digite 【Ctrl + g】 para abortar toda a operação.
  6. Tipo ! para replace todas as ocorrências no arquivo atual sem perguntar, N para pular todas as substituições possíveis para o resto do arquivo atual. (N é emacs 23 apenas)
  7. Para fazer a substituição em todos os arquivos sem mais perguntas, digite Y. (apenas para o Emacs 23)
  8. Chame 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

Selecione arquivos de destino

Inicie o emacs digitando “emacs” no prompt da interface da linha de comandos. (Ou, dê um duplo clique no ícone do Emacs se você estiver em um ambiente da Interface Gráfica com o Usuário)

Selecionando arquivos em um diretório

Primeiro você precisa selecionar os arquivos que deseja replace. Use o menu gráfico 〖File ▸ Open Directory〗. O Emacs perguntará por um caminho de diretório. Digite o caminho do diretório e pressione Enter.

Agora, você verá a lista de arquivos e, agora, você precisa marcar os arquivos nos quais deseja que o regex find / replace funcione. Marque um arquivo movendo o cursor para o arquivo desejado e pressione m. Desmarque-a pressionando u. (Para listar os 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 expressão regular, digite 【% m】 e digite seu padrão de expressão regular. Por exemplo, se você quiser marcar todos os arquivos HTML, digite 【% m】 e depois .html $. (Você pode encontrar uma lista dos comandos de marca no menu gráfico “Mark” (este menu aparece quando você está no modo dired)).

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

Se você quiser encontrar / replace arquivos dentro de um diretório, incluindo centenas de subdiretórios, aqui está um método para selecionar todos esses arquivos.

Ligue para o dir-find. (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 de tecla equivalente 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 com todos os arquivos desse diretório, dê “-type f”.

Agora, marque os arquivos conforme descrito acima.

Localização / Substituição Interativa

Agora, você está pronto para fazer a substituição de localização interativa. Por simplicidade, digamos que você queira apenas replace a palavra “rápido” por “super”. Agora, chame dired-do-query-replace-regexp. Ele irá solicitar a string regex e a string de substituição. Digite “quick”, digite e depois “super”.

Agora, o emacs usará seu padrão e verificará os arquivos, e parará e mostrará você sempre que uma correspondência ocorrer. Quando isso acontecer, o emacs perguntará a você e você terá a opção de fazer a mudança ou pular a mudança. Para fazer a alteração, digite y. Para pular, digite n. Se você quer simplesmente que o emacs vá em frente e faça todas as mudanças no arquivo atual, digite!

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

Salvando os Arquivos Alterados

Agora, depois que você passou pela provação acima, há mais uma etapa que você precisa fazer, e isso é salvar os arquivos alterados.

Se você estiver usando o emacs versão 22 ou posterior, chame ibuffer para entrar em um modo de listview de buffer, digite 【* u】 para marcar todos os arquivos não salvos e digite S para salvá-los. (isso é shift-s)

Se você estiver usando uma versão 21 do emacs, você pode fazer isso: chamar buffers de lista, mover o cursor para o arquivo que deseja salvar e digitar s. Ele marcará o arquivo para uma ação de salvamento posterior. 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 salvá-lo.

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

Projete é realmente bom: Cc pr executa o comando projectile-replace

MX Dired , e t para marcar todos os arquivos, e Q para consultar o texto de substituição em todos eles. Você pode expandir um subdiretório usando o comando i 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á arg e o argumento -R expandirá recursivamente todos os subdirs para o buffer dired . Então, agora você pode consultar todos os arquivos em um diretório inteiro.

Para buffers abertos, isso é o 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))))) 

Outra opção é usar a pesquisa de Icicles . Esse é um tipo diferente de pesquisa incremental que usa a conclusão de sua input minibuffer em relação aos hits de pesquisa. À medida que você modifica sua input atual, o conjunto de ocorrências correspondentes é atualizado no buffer *Completions* .

Você pode pesquisar qualquer quantidade de arquivos, buffers ou locais marcados, que você pode escolher usando o padrão minibuffer (por exemplo, regexp) correspondente.

Quando você visita um hit de busca, você pode replace on demand ou o hit inteiro ou apenas a parte dele que corresponde à sua input minibuffer atual. Substituição sob demanda significa que você não é consultado sobre cada hit de pesquisa, por sua vez; você acessa os hits que deseja diretamente, em qualquer ordem. Essa abordagem pode ser mais eficaz do que a substituição de consultas, se você tiver um número limitado de substituições a fazer: pular a consulta de forma exaustiva.

A pesquisa é sobre 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 no grep , mas um contexto pode ser qualquer bloco de texto com correspondência de padrão que você goste. Normalmente, você define os contextos de pesquisa usando um regexp, mas pode usar uma function. Além de definir os seus próprios, existem comandos de pesquisa de Icicles predefinidos para diferentes tipos de contexto: blocos de propriedades de texto ou propriedades de sobreposição, coisas no ponto, etc.

Você também pode classificar os hits de pesquisa em várias ordens de sorting para facilitar o access / navegação.

find-name-dired é OK, mas:

  • Todos os arquivos que você recebe correspondem ao mesmo, regexp único.
  • find-dired é mais flexível a esse respeito, mas também é feito para usar regras gerais (mesmo que elas possam ser arbitrariamente complexas). E, claro, find tem sua própria linguagem complexa.
  • Se você quiser então atuar apenas com alguns dos arquivos cujos nomes foram coletados no buffer de find(-name)-dired , você precisa marcá-los ou deletar / omitir as linhas daqueles que você não quer atuar.

Uma alternativa é usar os 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 lhe dá tanto generalidade quanto controle fácil sobre a escolha de arquivos. Estes comandos “here-and-below” 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 qualquer subdiretório, down, down, down …

Sim, uma alternativa ao uso de comandos here-and-below é inserir todos os subdiretórios e seus subdiretórios, recursivamente, e depois usar um comando de nível superior, como Q Mas muitas vezes pode ser conveniente não se incomodar com subdiretórios inseridos.

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

(Todos os comandos ” Dired + ” aqui e abaixo estão no menu Multiple > Marked Here e Below .)

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 Icicles, então você pode abrir um buffer Dired para apenas os arquivos em um conjunto de arquivos ou outros tipos de listas de arquivos salvos.

Você também pode marcar qualquer buffer Dired, incluindo um que você cria usando find(-name)-dired . Isso lhe dá uma maneira rápida de retornar a esse conjunto (por exemplo, um conjunto de projetos) mais tarde. E se você usar Bookmark +, em seguida, bookmarking um buffer de registros Dired (a) seus switches ls , (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. Bookmark + também permite marcar uma tree inteira de buffers Dired – saltar para o indicador restaura todos os buffers na tree.

Gostaria de sugerir mais uma excelente ferramenta que ainda não foi mencionada, nomeadamente Helm .

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

Basta abrir helm-find-files , marcar os arquivos relevantes com o M-SPC e, em seguida, usar F6 ou F7 para executar o helm-find-files query replace ou query replace regexp nos arquivos selecionados.

Não é Emacs, mas o xxdiff vem com uma ferramenta chamada xx-rename que fará isso para múltiplas strings de cada vez (por exemplo, de para para FROM), com comandos interativos, salvará backups de todos os arquivos modificados e produzirá um curto log de alterações feitas com contexto. É isso que costumo usar quando faço renomeamentos grandes / globais.