Repita um comando a cada intervalo de tempo no terminal?

143

Como posso repetir um comando a cada intervalo de tempo, para que eu possa executar comandos para verificar ou monitorar diretórios?

Não há necessidade de um script, preciso apenas de um comando simples para ser executado no terminal.

muru
fonte

Respostas:

220

Você pode usar o watchcomando, o watch é usado para executar qualquer comando designado em intervalos regulares.

Abra o Terminal e digite:

watch -n x <your command>

altere x para ser o tempo em segundos que você deseja.

Para obter mais ajuda sobre o watchcomando e suas opções, execute man watchou visite este link .

Por exemplo: a seguir, listamos, a cada 60s , no mesmo Terminal, o conteúdo do diretório Desktop, para que você possa saber se alguma alteração ocorreu:

watch -n 60 ls -l ~/Desktop
nux
fonte
34
+1, mas tenha cuidado ao usar expansões. Por exemplo, tente a diferença entre watch -n 1 'echo $COLUMNS'e watch -n 1 echo $COLUMNSao redimensionar seu terminal - o primeiro é expandido a cada segundo, mas o último é expandido apenas uma vez antes do watch início.
L0b0
Ele funciona como um encanto ! Ty nux
Problema que tenho com esta solução é que você não pode executar relógio como um processo e apenas deixá-lo rodando em segundo plano (por exemplo, com & renegar)
Cestarian
2
Existe alguma maneira de usar watchcom o comando do tipo "history enabled"? Adoro usar watch, mas às vezes prefiro ver um log de execuções anteriores, em vez de apenas a última. E sim, eu sei que posso usar scripting ( while true) para fazer isso, mas usar o watchutilitário é muito mais limpo!
Rinogo
isso funcionou para comandos mais simples, mas com comandos encadeados em encadeamento isso não funcionou para mim .. a seguir foi o comando que eu tentei => cat api.log | grep 'chamando' | wc -l
Sudip Bhandari
90

Você também pode usar este comando no terminal, além da resposta do nux:

while true; do <your_command>; sleep <interval_in_seconds>; done

Exemplo

while true; do ls; sleep 2; done

Este comando imprimirá a saída lsem um intervalo de 2 segundos.

Use Ctrl+ Cpara parar o processo.

Existem algumas desvantagens de watch

  • Não pode usar nenhum comando com alias.
  • Se a saída de qualquer comando for bastante longa, a rolagem não funcionará corretamente.
  • Há alguns problemas para definir o intervalo de tempo máximo além de determinado valor.
  • watchinterpretará seqüências de cores ANSI que passam caracteres de escape usando a opção -cou --color. Por exemplo, a saída de pygmentizefuncionará, mas falhará ls --color=auto.

Nas circunstâncias acima, isso pode parecer uma opção melhor.

souravc
fonte
watchexiste para isso, este é um pouco inútil, eu diria
de Bruno Pereira
14
Não estou afirmando que esta resposta deve ser usada em primeiro lugar. watché bom na maioria dos casos. É por isso que mencionei "além da resposta do nux" no começo. Mas existem alguns problemas com, watchpor exemplo, um não pode usar nenhum comando com aliaswatch . Tomemos, por exemplo, o llque é alias, ls -laFmas não pode ser usado com watch. Além disso, se a saída de qualquer comando for bastante longa, você terá problemas ao rolar usando watch. Nestes poucos casos especiais, esta resposta pode parecer uma opção melhor.
souravc
@souravc Minha versão de watchpelo menos permite as opções -cou --colorpara saída colorida.
Lily Chung
8
while sleep xé melhor - é mais fácil matar.
d33tah
3
Essa é uma boa alternativa e, ao contrário watch, mantém o histórico de comandos.
Adelriosantiago
34

Só queria entrar nas respostas de souravc e nux :

  1. Embora watchfuncione perfeitamente no Ubuntu, você pode evitar que, se quiser que seu "Unix-fu" seja puro - no FreeBSD, por exemplo, watch é um comando para "bisbilhotar em outra linha tty".
  2. while true; do command; sleep SECONDS; donetambém tem uma ressalva - o seu comando pode ser mais difícil de matar usando CTR + C . Você pode preferir while sleep SECONDS; do command; done- não é apenas mais curto, mas também mais fácil de interromper. A ressalva é que ele dormirá primeiro e, em seguida, execute seu comando, portanto, será necessário aguardar um pouco SECONDSpara que a primeira ocorrência do comando aconteça.
d33tah
fonte
2
Espero que seja aceitável como resposta em vez de comentário - eu queria mostrar outra solução aqui e comentar me daria menos atenção.
d33tah
Por que exatamente importa onde você coloca sleepo whileloop? Não encontrei nenhuma diferença, Ctrl + C interrompeu o ciclo instantaneamente, não importa o quê.
dessert
@dessert: depende do que você está tentando sair, eu acho. Normalmente, ctrl + c só iria matar o seu commande sleepe só quebrar se você matar true.
d33tah
11

Parece a tarefa ideal para o crondaemon, que permite executar comandos periódicos. Execute o crontab -ecomando para começar a editar a configuração do cron do usuário. Seu formato está documentado no crontab (5) . Basicamente, você tem cinco campos separados por espaço e relacionados ao tempo, seguidos por um comando:

The time and date fields are:

       field          allowed values
       -----          --------------
       minute         0-59
       hour           0-23
       day of month   1-31
       month          1-12 (or names, see below)
       day of week    0-7 (0 or 7 is Sunday, or use names)

Por exemplo, se você deseja executar um script Python toda terça-feira, às 11h:

0 11 * * 1 python ~/yourscript.py

Existem também alguns nomes especiais que substituem a hora, como @reboot. Muito útil se você precisar criar um diretório temporário. No meu crontab (listado com crontab -l):

# Creates a temporary directory for ~/.distcc at boot
@reboot ln -sfn "$(mktemp -d "/tmp/distcc.XXXXXXXX")" "$HOME/.distcc"
Lekensteyn
fonte
4
A pergunta pergunta como executar algo periodicamente no terminal. croné executado nos bastidores em vez de no terminal
northern-bradley
5

Se você está monitorando o sistema de arquivos, inotifywaité brilhante e certamente adiciona menos carga ao seu sistema.

Exemplo:

No terminal, digite este comando:

$ inotifywait .

Em seguida, no segundo terminal, qualquer comando que afete o diretório atual,

$ touch newfile

Em seguida, no terminal original, o inotifywait acordará e relatará o evento

./ CREATE newfile2

Ou em um loop

$ while true ; do inotifywait . ; done
Setting up watches.  
Watches established.
./ OPEN newfile2
Setting up watches.  
Watches established.
./ OPEN newfile2
Setting up watches.  
Watches established.
./ DELETE newfile
Setting up watches.  
Watches established.
./ CREATE,ISDIR newdir
Setting up watches.  
Watches established.
X Tian
fonte
o usuário lhe disse, sem roteiro, e talvez ele não quer monitorar qualquer coisa
nux
3
Eu não disse a ele para escrever um script, sugeri que, se eles estão fazendo um loop para assistir a um evento específico do sistema de arquivos, o inotifywait é útil e usa menos recursos do que repetir um comando. Costumo executar vários comandos em uma linha de comando, por exemplo, grep something InALogFile|lessé um script?
X Tian
é uma boa resposta, tente editá-lo para parecer mais simples.
Nux
3
O que poderia ser mais simples do que .eu não posso deixar de fora o comando.
X Tian
Obrigado @XTian, ​​um ótimo comando. Agora também vi na página do manual que você pode adicionar -mpara monitorar continuamente sem um loop.
yoniLavi
4

Você pode criar seu próprio repeatcomando, executando as seguintes etapas; créditos aqui :

Primeiro, abra seu .bash_aliasesarquivo:

$ xdg-open ~/.bash-aliases

Segundo, cole estas linhas na parte inferior do arquivo e salve:

repeat() {
n=$1
shift
while [ $(( n -= 1 )) -ge 0 ]
do
    "$@"
done
}

Terceiro, feche e abra novamente o seu terminal ou digite:

$ source ~/.bash_aliases

Et voilà! Agora você pode usá-lo assim:

$ repeat 5 echo Hello World !!!

ou

$ repeat 5 ./myscript.sh
Blufter
fonte
há um pequeno erro de digitação na linha xdg-open ~ / .bash-aliases. deve ser: xdg-open ~ / .bash_aliases (ou seja: sublinhado) #
ssinfod
4

você pode usar o crontab. execute o comando crontab -ee abra-o com o seu editor de texto preferido e adicione esta linha

*/10 * * * *  /path-to-your-command

Isso executará seu comando a cada 10 minutos

* */4 * * *  /path-to-your-command

Isso executará seu comando a cada 4 horas

Outra solução possível

$ ..some command...; for i in $(seq X); do $cmd; sleep Y; done

X número de vezes para repetir.

Y tempo para esperar para repetir.

Exemplo:

$ echo; for i in $(seq 5); do $cmd "This is echo number: $i"; sleep 1;done
Maythux
fonte
Por que isso é uma melhoria? Você acabou de adicionar uma etapa extra, desnecessária, salvando o comando como uma variável. As únicas coisas que isso faz é: i) prolongar a digitação; ii) obriga a usar apenas comandos simples, sem canais ou redirecionamentos, etc.
terdon
Remova a variável extra
Maythux 14/05
3

Outra preocupação com a abordagem de "observação" proposta acima é que ela exibe o resultado somente quando o processo é concluído. "date; sleep 58; date" exibirá as 2 datas somente após 59 segundos ... Se você iniciar algo em execução por 4 minutos, que exibe lentamente várias páginas de conteúdo, você realmente não o verá.

Por outro lado, a preocupação com a abordagem "enquanto" é que ela não leva em consideração a duração da tarefa.

while true; do script_that_take_between_10s_to_50s.sh; sleep 50; done

Com isso, o script será executado em algum momento a cada minuto, em algum momento poderá demorar 1m40. Portanto, mesmo que um cron possa executá-lo a cada minuto, aqui não será.

Portanto, para ver a saída no shell conforme ela é gerada e aguardar o tempo exato da solicitação, você precisa observar o tempo antes e depois e fazer um loop com o tempo.

Algo como:

while ( true ); do
  echo Date starting `date`
  before=`date +%s`
  sleep `echo $(( ( RANDOM % 30 )  + 1 ))`
  echo Before waiting `date`
  after=`date +%s`
  DELAY=`echo "60-($after-$before)" | bc`
  sleep $DELAY
  echo Done waiting `date`
done

Isso produzirá o seguinte:

Como você pode ver, o comando é executado a cada minuto:

Date starting Mon Dec 14 15:49:34 EST 2015
Before waiting Mon Dec 14 15:49:52 EST 2015
Done waiting Mon Dec 14 15:50:34 EST 2015

Date starting Mon Dec 14 15:50:34 EST 2015
Before waiting Mon Dec 14 15:50:39 EST 2015
Done waiting Mon Dec 14 15:51:34 EST 2015

Portanto, basta substituir o echo $(( ( RANDOM % 30 ) + 1 ))comando " sleep " pelo que você quiser e ele será executado, no terminal / shell, exatamente a cada minuto. Se você quiser outra programação, basta alterar os segundos "60" com o que você precisar.

Versão mais curta sem as linhas de depuração:

while ( true ); do
  before=`date +%s`
  sleep `echo $(( ( RANDOM % 30 )  + 1 ))` # Place you command here
  after=`date +%s`
  DELAY=`echo "60-($after-$before)" | bc`
  sleep $DELAY
done
jmspaggi
fonte
Respondendo a mim mesmo ... Se o seu comando demorar mais do que o atraso que você configurar, $ DELAY receberá um valor negativo e o comando sleep falhará, portanto o script será reiniciado imediatamente. Precisa estar ciente disso.
Jspaggi