¿Repetir un comando cada x intervalo de tiempo en la terminal?

143

¿Cómo puedo repetir un comando cada intervalo de tiempo, de modo que me permita ejecutar comandos para verificar o monitorear directorios?

No hay necesidad de un script, solo necesito un comando simple para ejecutar en la terminal.

muru
fuente

Respuestas:

220

Puede usar el watchcomando, watch se usa para ejecutar cualquier comando designado a intervalos regulares.

Abra la Terminal y escriba:

watch -n x <your command>

cambie x para que sea el tiempo en segundos que desee.

Para obtener más ayuda con el watchcomando y sus opciones, ejecute man watcho visite este enlace .

Por ejemplo: lo siguiente enumerará, cada 60s , en la misma Terminal, el contenido del directorio del Escritorio para que pueda saber si se produjo algún cambio:

watch -n 60 ls -l ~/Desktop
nux
fuente
34
+1 pero ten cuidado al usar expansiones. Por ejemplo, pruebe la diferencia entre watch -n 1 'echo $COLUMNS'y watch -n 1 echo $COLUMNSal cambiar el tamaño de su terminal: el primero se expande cada segundo, pero el segundo se expande solo una vez antes de watch comenzar.
l0b0
Funciona a las mil maravillas ! Ty nux
El problema que tengo con esta solución es que no puede ejecutar watch como un proceso y simplemente dejarlo ejecutándose en segundo plano (por ejemplo con &
disown
2
¿Hay alguna forma de usar watchcon el comando de tipo "historial habilitado"? Me encanta usar watch, pero a veces prefiero ver un registro de ejecuciones anteriores también, en lugar de solo la última. Y sí, sé que puedo usar scripting ( while true) para lograr esto, ¡pero usar la watchutilidad es mucho más limpio!
rinogo
esto funcionó para comandos más simples, pero con el encadenamiento de comandos canalizados esto no funcionó para mí ... el siguiente fue el comando que probé => cat api.log | grep 'llamando' | wc -l
Sudip Bhandari
90

También puede usar este comando en la terminal, aparte de la respuesta de nux:

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

Ejemplo

while true; do ls; sleep 2; done

Este comando imprimirá la salida lsen un intervalo de 2 segundos.

Use Ctrl+ Cpara detener el proceso.

Hay algunos inconvenientes de watch

  • No puede usar ningún comando con alias.
  • Si la salida de cualquier comando es bastante larga, el desplazamiento no funciona correctamente.
  • Hay algunos problemas para establecer el intervalo de tiempo máximo más allá de cierto valor.
  • watchinterpretará las secuencias de color ANSI que pasan caracteres de escape utilizando -cu --coloropción. Por ejemplo, la salida de pygmentizefuncionará pero fallará ls --color=auto.

En las circunstancias anteriores, esto puede aparecer como una mejor opción.

souravc
fuente
watchexiste para eso, esto es un poco inútil, diría yo
Bruno Pereira
14
No estoy afirmando que esta respuesta se use en primer lugar. watchEs bueno en la mayoría de los casos. Es por eso que mencioné "aparte de la respuesta de nux" al principio. Pero hay algunos problemas con, watchpor ejemplo, uno no puede usar ningún comando con aliaswatch . Tomemos, por ejemplo, un llalias ls -laFpero que no se puede usar con watch. Además, en caso de que la salida de cualquier comando sea bastante larga, tendrá problemas para desplazarse usando watch. En estos pocos casos especiales, esta respuesta puede parecer una mejor opción.
souravc
@souravc Mi versión de watchal menos permite las opciones -cu --colorpara salida coloreada.
Lily Chung
8
while sleep xes mejor, es más fácil de matar.
d33tah
3
Esta es una buena alternativa y, a diferencia watch, mantiene el historial de comandos.
adelriosantiago
34

Solo quería participar en las respuestas de souravc y nux :

  1. Si bien watchfuncionará perfectamente en Ubuntu, es posible que desee evitar eso si desea que su "Unix-fu" sea puro, por ejemplo, en FreeBSD, watch es un comando para "espiar en otra línea tty".
  2. while true; do command; sleep SECONDS; doneTambién tiene una advertencia - el comando podría ser más difícil de matar usando CTR + C . Es posible que desee preferir while sleep SECONDS; do command; done: no solo es más corto, sino también más fácil de interrumpir. La advertencia es que primero dormirá, luego ejecutará su comando, por lo que deberá esperar un poco SECONDSantes de que ocurra el primer comando.
d33tah
fuente
2
Espero que sea aceptable como respuesta en lugar de un comentario: quería mostrar otra solución aquí y comentar realmente me daría menos atención.
d33tah
¿Por qué exactamente importa dónde pones sleepen el whilebucle? No pude encontrar ninguna diferencia, Ctrl + C rompió el ciclo instantáneamente sin importar qué.
postre
@dessert: depende de lo que intentas romper, supongo. Normalmente, ctrl + c simplemente mataría a tu commandy sleepy solo se rompería si matas true.
d33tah
11

Suena como la tarea ideal para el crondemonio que permite ejecutar comandos periódicos. Ejecute el crontab -ecomando para comenzar a editar la configuración cron de su usuario. Su formato está documentado en crontab (5) . Básicamente tiene cinco campos separados por espacios y relacionados con el tiempo seguidos de un 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 ejemplo, si desea ejecutar un script de Python todos los martes a las 11 AM:

0 11 * * 1 python ~/yourscript.py

También hay algunos nombres especiales que reemplazan el tiempo, como @reboot. Muy útil si necesita crear un directorio temporal. Desde mi crontab (listado con crontab -l):

# Creates a temporary directory for ~/.distcc at boot
@reboot ln -sfn "$(mktemp -d "/tmp/distcc.XXXXXXXX")" "$HOME/.distcc"
Lekensteyn
fuente
44
La pregunta pregunta cómo ejecutar algo periódicamente en la terminal. croncorre detrás de las escenas en lugar de en la terminal
norte-Bradley
5

Si está monitoreando el sistema de archivos, entonces inotifywaites brillante y ciertamente agrega menos carga en su sistema.

Ejemplo:

En el primer terminal, escriba este comando:

$ inotifywait .

Luego, en la segunda terminal, cualquier comando que afecte el directorio actual,

$ touch newfile

Luego, en la terminal original, Inotifywait se despertará e informará el evento

./ CREATE newfile2

O en un bucle

$ 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
fuente
el usuario que, sin guión le dijo, y tal vez él no desea monitorizar cualquier cosa
Nux
3
No le dije que escribiera un script, le sugerí que si están haciendo un bucle para observar un evento particular del sistema de archivos, entonces inotifywait es útil y usa menos recursos que repetir un comando. A menudo ejecuto varios comandos en una línea de comando, por ejemplo, ¿ grep something InALogFile|lesses un script?
X Tian
es una buena respuesta, intenta editarlo para que parezca más simple.
nux
3
Lo que podría ser más simple que .no puedo dejar de lado el comando.
X Tian
Gracias @XTian, ​​un gran comando. También vi en la página de manual que puede agregar -mpara monitorear continuamente sin un bucle.
yoniLavi
4

Puede crear su propio repeatcomando siguiendo los siguientes pasos; créditos aquí :

Primero, abra su .bash_aliasesarchivo:

$ xdg-open ~/.bash-aliases

En segundo lugar, pegue estas líneas en la parte inferior del archivo y guarde:

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

Tercero, cierre y abra nuevamente su terminal o escriba:

$ source ~/.bash_aliases

Et voilà! Ahora puede usarlo así:

$ repeat 5 echo Hello World !!!

o

$ repeat 5 ./myscript.sh
Blufter
fuente
Hay un pequeño error tipográfico en la línea xdg-open ~ / .bash-aliases. debería ser: xdg-open ~ / .bash_aliases (es decir: subrayado)
ssinfod
4

puedes usar crontab. ejecute el comando crontab -ey ábralo con su editor de texto preferido, luego agregue esta línea

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

Esto ejecutará tu comando cada 10 minutos

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

Esto ejecutará su comando cada 4 horas.

Otra posible solución

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

X número de veces para repetir.

Y tiempo de esperar para repetir.

Ejemplo:

$ echo; for i in $(seq 5); do $cmd "This is echo number: $i"; sleep 1;done
Maythux
fuente
¿Por qué es esto una mejora? Acaba de agregar un paso adicional, innecesario, guardando el comando como una variable. Lo único que hace es i) alargar el tipo ii) te obliga a usar solo comandos simples, sin canalizaciones o redirecciones, etc.
terdon
Eliminar la variable adicional
Maythux
3

Otra preocupación con el enfoque de "observación" propuesto anteriormente es que muestra el resultado solo cuando se realiza el proceso. "date; sleep 58; date" mostrará las 2 fechas solo después de 59 segundos ... Si comienza a ejecutar algo durante 4 minutos, que muestra lentamente varias páginas de contenido, realmente no lo verá.

Por otro lado, la preocupación con el enfoque "while" es que no toma en cuenta la duración de la tarea.

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

Con esto, el script se ejecutará en algún momento cada minuto, en algún momento puede tomar 1m40. Entonces, incluso si un cron podrá ejecutarlo cada minuto, aquí no lo hará.

Entonces, para ver la salida en el shell a medida que se genera y esperar el tiempo de solicitud exacto, debe mirar el tiempo antes y después, y recorrer el tiempo.

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

Esto generará esto:

Como puede ver, el comando se ejecuta 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

Así que simplemente reemplace el echo $(( ( RANDOM % 30 ) + 1 ))comando "dormir " con lo que quiera y se ejecutará, en el terminal / shell, exactamente cada minuto. Si desea otro horario, simplemente cambie los "60" segundos con lo que necesite.

Versión más corta sin las líneas de depuración:

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
fuente
Respondiéndome a mí mismo ... Si su comando tarda más que el retraso que configuró, $ DELAY obtendrá un valor negativo y el comando de suspensión fallará, por lo que el script se reiniciará de inmediato. Necesito estar al tanto de eso.
jmspaggi