Eu quero uma maneira rápida e simples de executar um comando sempre que um arquivo for alterado. Quero algo muito simples, algo que deixarei em execução em um terminal e fechá-lo sempre que terminar de trabalhar com esse arquivo.
Atualmente, estou usando isso:
while read; do ./myfile.py ; done
E então eu preciso ir ao terminal e pressionar Enter, sempre que salvar esse arquivo no meu editor. O que eu quero é algo como isto:
while sleep_until_file_has_changed myfile.py ; do ./myfile.py ; done
Ou qualquer outra solução tão fácil quanto isso.
BTW: Estou usando o Vim e sei que posso adicionar um comando automático para executar algo no BufWrite, mas esse não é o tipo de solução que eu quero agora.
Atualização: eu quero algo simples, descartável, se possível. Além do mais, quero que algo seja executado em um terminal porque quero ver a saída do programa (quero ver mensagens de erro).
Sobre as respostas: Obrigado por todas as suas respostas! Todos eles são muito bons e cada um tem uma abordagem muito diferente dos outros. Como preciso aceitar apenas um, aceito o que realmente usei (era simples, rápido e fácil de lembrar), mesmo sabendo que não é o mais elegante.
Respostas:
Simples, usando inotifywait (instale o
inotify-tools
pacote da sua distribuição ):ou
O primeiro snippet é mais simples, mas tem uma desvantagem significativa: ele perderá as alterações executadas enquanto
inotifywait
não estiver em execução (em particular enquantomyfile
estiver em execução). O segundo trecho não possui esse defeito. No entanto, lembre-se de que o nome do arquivo não contenha espaço em branco. Se houver algum problema, use a--format
opção para alterar a saída para não incluir o nome do arquivo:De qualquer maneira, há uma limitação: se algum programa substitui
myfile.py
com um arquivo diferente, ao invés de escrever ao existentemyfile
,inotifywait
vai morrer. Muitos editores trabalham dessa maneira.Para superar essa limitação, use
inotifywait
no diretório:Como alternativa, use outra ferramenta que use a mesma funcionalidade subjacente, como incron (permite registrar eventos quando um arquivo é modificado) ou fswatch (uma ferramenta que também funciona em muitas outras variantes do Unix, usando o análogo de cada variante do inotify do Linux).
fonte
sleep_until_modified.sh
script simples de usar , disponível em: bitbucket.org/denilsonsa/small_scripts/srcwhile sleep_until_modified.sh derivation.tex ; do latexmk -pdf derivation.tex ; done
é fantástico. Obrigado.inotifywait -e delete_self
parece funcionar bem para mim.while inotifywait -e close_write myfile.py; do ./myfile.py; done
sempre sai sem executar o comando (bash e zsh). Para que isso funcionasse, eu precisava adicionar|| true
, por exemplo:while inotifywait -e close_write myfile.py || true; do ./myfile.py; done
entr ( http://entrproject.org/ ) fornece uma interface mais amigável para inotify (e também suporta * BSD e Mac OS X).
Torna muito fácil especificar vários arquivos para assistir (limitado apenas por
ulimit -n
), elimina o incômodo de lidar com a substituição de arquivos e requer menos sintaxe do bash:Eu o uso em toda a árvore de origem do projeto para executar os testes de unidade do código que estou modificando no momento, e isso já foi um grande impulso para o meu fluxo de trabalho.
Sinalizadores como
-c
(limpar a tela entre execuções) e-d
(sair quando um novo arquivo é adicionado a um diretório monitorado) adicionam ainda mais flexibilidade, por exemplo, você pode:A partir do início de 2018, ele ainda está em desenvolvimento ativo e pode ser encontrado no Debian & Ubuntu (
apt install entr
); construção do repositório do autor foi indolor em qualquer caso.fonte
-d
bandeira; é um pouco mais demorado, mas você pode fazerwhile sleep 1 ; do find . -name '*.py' | entr -d ./myfile.py ; done
para lidar com novos arquivos.brew install entr
funcionará como esperadoEu escrevi um programa Python para fazer exatamente isso chamado quando alterado .
O uso é simples:
Ou para assistir a vários arquivos:
FILE
pode ser um diretório Assista recursivamente com-r
. Use%f
para passar o nome do arquivo para o comandofonte
when-changed FILE 'clear; COMMAND'
.when-changed
agora é multiplataforma! Confira a versão mais recente do 0.3.0 :)E esse script? Ele usa o
stat
comando para obter o tempo de acesso de um arquivo e executa um comando sempre que houver uma alteração no tempo de acesso (sempre que o arquivo for acessado).fonte
stat
hora modificada não seria uma resposta melhor "sempre que um arquivo for alterado"?vmstat -d
observe o acesso ao disco. Parece linux faz um trabalho fantástico em cache esse tipo de coisa: DSolução usando o Vim:
Mas eu não quero essa solução porque é meio chato digitar, é um pouco difícil lembrar o que digitar, exatamente, e é um pouco difícil desfazer seus efeitos (é necessário executar
:au! BufWritePost myfile.py
). Além disso, esta solução bloqueia o Vim até que o comando termine de executar.Adicionei esta solução aqui apenas por questões de integridade, pois pode ajudar outras pessoas.
Para exibir a saída do programa (e interromper completamente o fluxo de edição, como a saída gravará sobre seu editor por alguns segundos, até você pressionar Enter), remova o
:silent
comando.fonte
entr
(veja abaixo) - apenas faça com que o vim toque em um arquivo fictício que entr está assistindo e deixe entr fazer o resto em segundo plano ... outmux send-keys
se você estiver em um ambiente como esse :).vimrc
arquivoSe você
npm
instalou,nodemon
provavelmente é a maneira mais fácil de começar, especialmente no OS X, que aparentemente não possui ferramentas para inotificação. Ele suporta a execução de um comando quando uma pasta é alterada.fonte
nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models
WatchPaths
chave, como mostrado no meu link.Para aqueles que não podem instalar
inotify-tools
como eu, isso deve ser útil:watch -d -t -g ls -lR
Este comando sairá quando a saída for alterada,
ls -lR
listará todos os arquivos e diretórios com seu tamanho e datas; portanto, se um arquivo for alterado, ele deverá sair do comando, como o homem diz:Sei que esta resposta pode não ser lida por ninguém, mas espero que alguém a atinja.
Exemplo de linha de comando:
Abra outro terminal:
Agora o primeiro terminal será emitido
1,2,3
Exemplo de script simples:
fonte
watch -d -t -g "ls -lR tmp | sha1sum"
watch from procps-ng 3.3.10
) aceita segundos flutuantes por seu intervalo, portantowatch -n0.2 ...
, pesquisará a cada quinto de segundo. Bom para os testes de unidade saudáveis de milissegundos.rerun2
( no github ) é um script Bash de 10 linhas no formato:Salve a versão do github como 'reexecutar' no seu PATH e invoque-a usando:
Ele executa o COMMAND toda vez que há um evento de modificação do sistema de arquivos no diretório atual (recursivo).
Coisas que alguém pode gostar sobre isso:
Coisas que alguém pode não gostar:
Este é um refinamento da resposta de @ cychoi.
fonte
"$@"
vez de$@
, a fim de trabalhar corretamente com argumentos que contêm espaços. Mas, ao mesmo tempo que você usaeval
, o que força o usuário a executar novamente a ter um cuidado extra ao citar.rerun 'command'
. Você está apenas dizendo que, se eu usasse "$ @", o usuário poderia chamar comorerun command
(sem aspas?) Isso não parece tão útil para mim: geralmente não quero que o Bash faça nenhum processamento de comando antes de passá-lo para executar novamente. por exemplo, se o comando contiver "echo $ myvar", desejarei ver os novos valores de myvar em cada iteração.rerun foo "Some File"
pode quebrar. Mas como você está usandoeval
, ele pode ser reescrito comorerun 'foo "Some File"
. Observe que, às vezes, a expansão do caminho pode introduzir espaços:rerun touch *.foo
provavelmente será interrompida, e o usorerun 'touch *.foo'
tem semântica ligeiramente diferente (a expansão do caminho ocorre apenas uma vez ou várias vezes).rerun ls "some file"
quebra por causa dos espaços.rerun touch *.foo*
normalmente funciona bem, mas falha se os nomes de arquivos que correspondem a * .foo contiverem espaços. Obrigado por me ajudar a ver comorerun 'touch *.foo'
tem semântica diferente, mas suspeito que a versão com aspas simples seja a semântica que eu quero: quero que cada iteração de reexecução aja como se eu tivesse digitado o comando novamente - portanto, quero*.foo
ser expandida em cada iteração . Vou tentar as suas sugestões para examinar os seus efeitos ...Aqui está um simples shell shell Bourne que:
Limpa depois de pressionar quando você pressiona Ctr-C
Isso funciona no FreeBSD. O único problema de portabilidade que consigo pensar é se algum outro Unix não possui o comando mktemp (1), mas nesse caso você pode apenas codificar o nome do arquivo temporário.
fonte
$cmd
, mas felizmente isso é facilmente corrigível: abandone acmd
variável e execute"$@"
. Seu script não é adequado para monitorar um arquivo grande, mas pode ser corrigido substituindocp
portouch -r
(você só precisa da data, não do conteúdo). Em termos de portabilidade, o-nt
teste requer bash, ksh ou zsh.Dê uma olhada no incron . É semelhante ao cron, mas usa eventos inotify em vez de tempo.
fonte
Outra solução com os NodeJs, fsmonitor :
Instalar
Na linha de comando (exemplo, monitore logs e "revenda" se um arquivo de log for alterado)
fonte
tail -F -q *.log
, eu acho.tail -f
não éclear
o terminal.Veja o Guard, em particular com este plugin:
https://github.com/hawx/guard-shell
Você pode configurá-lo para assistir a qualquer número de padrões no diretório do seu projeto e executar comandos quando ocorrerem alterações. Boa chance, mesmo que haja um plugin disponível para o que você está tentando fazer em primeiro lugar.
fonte
se você tiver o nodemon instalado, poderá fazer o seguinte:
No meu caso, edito o html localmente e o envio para o servidor remoto quando um arquivo é alterado.
fonte
No Linux:
Irá executar o comando a cada 2 segundos.
Se o seu comando levar mais de 2 segundos para ser executado, o relógio aguardará até que seja concluído antes de executar novamente.
fonte
Watchdog é um projeto Python e pode ser exatamente o que você está procurando:
Acabei de escrever um wrapper de linha de comando para ele
watchdog_exec
:Execuções de exemplo
No evento fs que envolve arquivos e pastas no diretório atual, execute o
echo $src $dst
comando, a menos que o evento fs seja modificado, execute opython $src
comando.Usando argumentos curtos e restringindo a execução apenas quando eventos envolvem " main .py":
EDIT: Acabei de encontrar Watchdog tem uma CLI oficial chamada
watchmedo
, então confira também.fonte
Se o seu programa gerar algum tipo de log / saída, você pode criar um Makefile com uma regra para esse log / saída que depende do seu script e fazer algo como
Como alternativa, você pode criar um alvo falso e ter a regra de que ele chame seu script e toque no alvo falso (enquanto ainda depende do seu script).
fonte
while sleep 1 ; do something ; done
é um pouco melhor quewhile true ; do something ; sleep 1 ; done
. Pelo menos para com facilidade ao pressionar Ctrl + C.swarminglogic escreveu um script chamado watchfile.sh , também disponível como GistHub Gist .
fonte
stat
os nomes de arquivos fornecidos, executamd5sum
na saída e executa novamente o comando fornecido, se esse valor for alterado. Por ser o Bash, suspeito que ele execute um bom comando exatamente como se você o digitasse em um prompt do Bash. (Em contraste, a maioria das soluções aqui escritos em outras línguas falhará para executar comandos que, por exemplo, contêm pseudónimos de casca, tais comoll
)Melhorado com a resposta de Gilles .
Esta versão é executada
inotifywait
uma vez e monitora os eventos (.eg:)modify
posteriormente. Tais queinotifywait
não precisam ser reexecutados a cada evento encontrado.É rápido e rápido! (mesmo ao monitorar diretório grande recursivamente)
fonte
Um pouco mais sobre o lado da programação, mas você deseja algo como inotify . Existem implementações em vários idiomas, como jnotify e pyinotify .
Essa biblioteca permite monitorar arquivos únicos ou diretórios inteiros e retorna eventos quando uma ação é descoberta. As informações retornadas incluem o nome do arquivo, a ação (criar, modificar, renomear, excluir) e o caminho do arquivo, entre outras informações úteis.
fonte
Para aqueles que procuram uma solução FreeBSD, aqui está a porta:
fonte
Eu gosto da simplicidade de,
while inotifywait ...; do ...; done
no entanto, tem dois problemas:do ...;
serão perdidasPara isso, criei um script auxiliar que usa inotifywait sem essas limitações: inotifyexec
Eu sugiro que você coloque esse script no seu caminho, como em
~/bin/
. O uso é descrito apenas executando o comando.Exemplo:
inotifyexec "echo test" -r .
fonte
Solução aprimorada de Sebastian com
watch
comando:watch_cmd.sh
:Exemplo de chamada:
Funciona, mas tenha cuidado: o
watch
comando conhece bugs (consulte man): ele reage às alterações apenas no VISÍVEL nas partes terminais da-g CMD
saída.fonte
Você pode tentar reflexo .
O Reflex é uma pequena ferramenta para monitorar um diretório e executar novamente um comando quando certos arquivos são alterados. É ótimo para executar automaticamente tarefas de compilação / limpeza / teste e para recarregar seu aplicativo quando o código é alterado.
fonte
Uma resposta oneliner que estou usando para acompanhar uma alteração no arquivo:
Você não precisa inicializar o BF se souber que a primeira data é a hora de início.
Isso é simples e portátil. Há outra resposta baseada na mesma estratégia usando um script aqui. Dê uma olhada também.
Uso: estou usando isso para depurar e ficar de olho
~/.kde/share/config/plasma-desktop-appletsrc
; que por algum motivo desconhecido continua perdendo meuSwitchTabsOnHover=false
fonte
Eu uso esse script para fazer isso. Estou usando inotify no modo monitor
Salve isso como runatwrite.sh
ele executará myfile.sh a cada gravação.
fonte
Para aqueles que usam o OS X, você pode usar o LaunchAgent para observar um caminho / arquivo em busca de alterações e fazer algo quando isso acontecer. FYI - LaunchControl é um bom aplicativo para criar / modificar / remover daemons / agentes facilmente.
( exemplo retirado daqui )
fonte
Eu tenho um GIST para isso e o uso é bastante simples
https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55
fonte
Para as pessoas que acham isso pesquisando para alterações em um determinado arquivo, a resposta é muito mais simples (inspirado resposta de Gilles ).
Se você deseja fazer algo após a gravação de um arquivo específico, veja como:
Salve isso como, por exemplo,
copy_myfile.sh
e coloque o.sh
arquivo na/etc/init.d/
pasta para que ele seja executado na inicialização.fonte
A ferramenta 'fido' pode ser mais uma opção para essa necessidade. Veja https://www.joedog.org/fido-home/
fonte
Como algumas outras pessoas fizeram, também escrevi uma ferramenta leve de linha de comando para fazer isso. Está totalmente documentado, testado e modular.
Watch-Do
Instalação
Você pode instalá-lo (se você tiver Python3 e pip) usando:
Uso
Use-o imediatamente executando:
Visão geral dos recursos
-w '*.py'
ou-w '**/*.py'
)-d
sinalizador novamente)-r
para ativar isso)fonte