Répéter une commande Unix toutes les x secondes pour toujours


481

Il existe une commande Unix intégrée repeatdont le premier argument est le nombre de répétitions d'une commande, la commande (avec tous les arguments) étant spécifiée par les arguments restants repeat.

Par exemple,

% repeat 100 echo "I will not automate this punishment."

fera écho la chaîne donnée 100 fois puis s'arrêtera.

J'aimerais une commande similaire - appelons-la forever- qui fonctionne de la même manière, sauf que le premier argument est le nombre de secondes de pause entre les répétitions et qu'il se répète indéfiniment. Par exemple,

% forever 5 echo "This will get echoed every 5 seconds forever and ever."

Je pensais demander si une telle chose existait avant de l'écrire. Je sais que c'est comme un script Perl ou Python à 2 lignes, mais il existe peut-être une méthode plus standard pour le faire. Si ce n'est pas le cas, n'hésitez pas à poster une solution dans votre langage de script préféré, à la manière de Rosetta Stone .

PS: Une meilleure façon de procéder serait peut-être de généraliser repeaten prenant à la fois le nombre de répétitions (-1 signifiant l'infini) et le nombre de secondes de veille entre les répétitions. Les exemples ci-dessus deviendraient alors:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."

1
Pourquoi pas: répéter la commande [-t time] [-n number] [args ...]?
Jonathan Leffler

17
Cool. Malheureusement, à la fois sur mon Ubuntu et sur ma répétition AIX, la commande est introuvable . Pouvez-vous faire un type repeatet laissez-moi savoir d'où vient?

3
Je suis aussi sur Ubuntu et je n'ai pas installé de répétition. Toute information sur la façon d'installer ceci serait utile.
Rory

7
repeatest une commande intégrée dans csh et tcsh.
Keith Thompson

2
@ KeithThompson, csh, tcsh et zsh . Bien que ce soit plus un mot-clé qu'un mot clé intégré
Stéphane Chazelas

Réponses:


618

Essayez la watchcommande.

Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] 
             [--no-title] [--version] <command>`

Pour que:

watch -n1  command

exécutera la commande toutes les secondes (enfin, techniquement, toutes les secondes plus le temps nécessaire commandà l'exécution de watch(au moins, les implémentations procpset busybox) dort une seconde entre deux exécutions command), pour toujours.

Souhaitez-vous passer la commande à la execplace de sh -c, utilisez l' -xoption:

watch -n1 -x command

Sur macOS, vous pouvez obtenir watchdes ports Mac :

port install watch

Ou vous pouvez l'obtenir de Homebrew :

brew install watch

2
vous pouvez utiliser MacPorts pour l'installer. sudo port installer watch

10
Également disponible en homebrew (montre à installer).
Lyle

28
+1 Pour un nouvel outil puissant. J'avais l'habitude de while true; do X; sleep Y; done- C'est beaucoup mieux.
Adam Matan

4
Le problème avec cet outil (du moins sur Ubuntu), c’est qu’il force le plein écran, donc si je vide des informations périodiques, je perds les informations précédentes.
scorpiodawg

4
Il s’appelle cmdwatchsur FreeBSD (depuis les ports:) sysutils/cmdwatch.
Ouki

244

Frapper

while+ sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Voici la même chose qu'un raccourci One-Liner (d'après les commentaires ci-dessous):

while sleep 1; do echo "Hi"; done

Utilise ;pour séparer les commandes et utilise sleep 1pour le whiletest puisqu'il renvoie toujours true. Vous pouvez mettre plus de commandes dans la boucle - séparez-les simplement avec;


2
Je crois que cela fonctionne comme écrit dans un shell générique Bourne, aussi bien.
dmckee

5
@dreeves, en utilisant ";" en tant que séparateur, les commandes peuvent être exécutées sur une seule ligne. Regardez la réponse de Toony à titre d'exemple
bbaja42

57
sleep 1 retourne toujours true, vous pouvez donc l'utiliser comme condition while. Ce n’est pas la chose la plus lisible au monde, mais elle est tout à fait légitime: pendant le sommeil 1; echo "Salut"; terminé
David Costa

3
@ David Costa: Vous avez fait valoir un point raisonnable, mais vous sleepn'y retournez pas toujours . Ī̲ a utilisé de telles boucles (bien qu'avec des délais plus longs), notamment parce qu'il existe un moyen de les terminer gracieusement en mettant fin au processus de sommeil.
Incnis Mrsi

2
@IncnisMrsi Je n'y ai pas pensé, il renvoie zéro uniquement lorsque le temps est écoulé et différent de zéro lorsqu'il se termine par un signal. Merci de me l'avoir signalé
David Costa

72

Ceci est juste une version abrégée d’autres while+sleepréponses, si vous exécutez ce type de tâches souvent comme votre routine quotidienne, vous évitez ainsi d’appuyer sur des touches inutiles et si votre ligne de commande commence à être plus longue, sachant que celle-ci est un peu plus facile. Mais celui-ci commence par dormir en premier.

Ceci est généralement utile si vous devez suivre quelque chose qui a une sortie sur une ligne, telle que la charge de la machine:

while sleep 1; do uptime; done

Oui. Cela devrait être documenté à côté de UUOC.
Johan

10
Et si vous voulez que cela fonctionne comme une "montre", vous pouvez le fairewhile clear; do date; command;sleep 5; done
Johan

Wow, merci pour la while sleepcombinaison, enregistre les keystokes!
George Y.

45

Un problème que toutes les réponses postées jusqu'à présent ont, c'est que le temps d'exécution de la commande peut dériver. Par exemple, si vous faites une sleep 10commande entre deux et que la commande prend 2 secondes pour s'exécuter, elle s'exécutera toutes les 12 secondes. s'il faut un temps variable pour s'exécuter, le temps d'exécution peut être imprévisible à long terme.

Cela pourrait être juste ce que vous voulez; si tel est le cas, utilisez l’une des autres solutions ou utilisez-la, mais simplifiez l’ sleepappel.

Pour une résolution d'une minute, les tâches cron seront exécutées à l'heure spécifiée, quelle que soit la durée de chaque commande. (En fait, un nouveau travail cron sera lancé même si le précédent est toujours en cours d'exécution.)

Voici un script Perl simple qui dort jusqu'à l'intervalle suivant. Ainsi, par exemple, avec un intervalle de 10 secondes, la commande peut s'exécuter à 12:34:00, 12:34:10, 12:34:20, etc., même si le La commande elle-même prend plusieurs secondes. Si la commande s'exécute plus de intervalsecondes, l'intervalle suivant sera ignoré (contrairement à cron). L'intervalle étant calculé par rapport à l'époque, un intervalle de 86 400 secondes (1 jour) sera exécuté à minuit UTC.

#!/usr/bin/perl

use strict;
use warnings;

if (scalar @ARGV < 2) {
    die "Usage: $0 seconds command [args...]\n";
}

$| = 1;  # Ensure output appears

my($interval, @command) = @ARGV;

# print ">>> interval=$interval command=(@command)\n";

while (1) {
    print "sleep ", $interval - time % $interval, "\n";
    sleep $interval - time % $interval;
    system @command; # TODO: Handle errors (how?)
}


1
plus simple en bash pour minimiser la dérive (bien qu'il puisse dériver sur de très longues fenêtres d'utilisation en raison de la commande de veille et que bash lui-même accumule ses temps d'exécution minuscules): while :; do sleep 1m & command; wait; done
maths

1
@math: Clever. Cela ne fonctionne pas si d'autres processus en arrière-plan s'exécutent dans le shell actuel ( waitils les attendront tous). C'est réparable en changeant waità wait $!, où $!est le PID du sleepprocessus. Postez ceci comme une réponse et je l’envois. Mais cela produit des sorties superflues dans un shell interactif.
Keith Thompson

29

Je pense que toutes les réponses à ce jour sont soit trop compliquées, soit répondent à une question différente:

  • "Comment faire pour exécuter un programme à plusieurs reprises afin qu'il y ait un délai de X secondes entre la fin du programme et le prochain démarrage" .

La vraie question était:

  • "Comment exécuter un programme toutes les X secondes"

Ce sont deux choses très différentes lorsque la commande met du temps à se terminer.

Prenons par exemple le script foo.sh(supposons qu'il s'agit d'un programme qui prend quelques secondes à terminer).

#!/bin/bash
# foo.sh
echo `date +"%H:%M:%S"` >> output.txt;
sleep 2.5;
# ---

Vous souhaitez exécuter cela toutes les secondes, et la plupart suggèrent watch -n1 ./foo.sh, ou while sleep 1; do ./foo.sh; done. Cependant, cela donne la sortie:

15:21:20
15:21:23
15:21:27
15:21:30
15:21:34

Ce qui ne se fait pas exactement à chaque seconde. Même avec le -pdrapeau, comme le man watchsuggère la page, le résultat est le même.

Un moyen facile d'accomplir la tâche souhaitée, que certains abordent, consiste à exécuter la commande en arrière-plan. En d'autres termes:

while sleep 1; do (./foo.sh &) ; done

Et c'est tout ce qu'il y a à faire.

Vous pouvez l'exécuter toutes les 500 ms avec sleep 0.5, ou ce que vous avez.


1
Ceux qui ont voté sur des réponses différentes, ne comprennent pas l'élégance de votre réponse ...
Serge Stroobandt

C'est bien sauf si votre programme a des effets secondaires. Si le programme prend plus de temps que l'intervalle, les effets secondaires peuvent interférer (comme écraser un fichier). Si le programme attend quelque chose d'autre et que ce processus prend une éternité, vous commencez à créer de plus en plus de travaux en tâche de fond indéfiniment. Utiliser avec précaution.
John Moeller

4
pourrait inverser l'ordre des arrière-plans pour s'assurer que pas plus d'un ./foo.sh n'est exécuté à la fois, mais pas plus rapide que 1 par seconde: while :; do sleep 1 & ./foo.sh; wait; done
math

Oui, cela va dériver, quelques millisecondes sur chaque boucle, mais ça le fera. Faites date +"%H:%M:%S.%N"dans foo.sh pour voir comment la fraction augmentera lentement à chaque boucle.
Isaac

Oui, il est vrai que la plupart des réponses ne traitent pas littéralement la question. Mais il y a fort à parier qu'ils répondent à ce que le PO voulait. Ce serait un pari plus sûr si le PO avait effectivement accepté une réponse ... Mais c'est une bonne chose , à condition que vous connaissiez les éventuels effets secondaires et que vous soyez certain que le temps moyen d'exécution de la commande ne dépasse pas le temps de cycle.
Auspex

17

Dans bash:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; done'

( echopourrait être remplacé par n'importe quelle commande ...

Ou dans perl:

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"}'

print "I will not automate this punishment in absurdum.\n"pourrait être remplacé par "toute" commande entourée de backticks (`).

Et pour une pause, ajoutez une sleepdéclaration dans la boucle for:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; sleep 1; done'

et

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'

16
while [ 0 ]est une mauvaise façon de l'écrire. Cela signifie 0est une chaîne de longueur> 0. while [ 1 ]ou while [ jeff ]ferait la même chose. Mieux vaut écrire while true.
Mikel

5
@ Mikel: Ouwhile :
Keith Thompson

10

Recent bash> = 4.2 sous noyau récent, réponse basée.

Afin de limiter le temps d'exécution, il n'y a pas de fourches ! Seuls les éléments intégrés sont utilisés.

Pour cela, readj'utilise la fonction intégrée au lieu de sleep. Unfortunely cela ne fonctionnera pas avec Notty sessions.

Fonction rapide " repeat" à la demande:

repeat () {
   local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep
   read -t .0001 repeat_foo
   if [ $? = 1 ] ;then
       repeat_sleep() { sleep $1 ;}
   else
       repeat_sleep() { read -t $1 repeat_foo; }
   fi
   shift 2
   while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times))&& ((10#${repeat_delay//.})) &&
            repeat_sleep $repeat_delay
   done
}

Petit test avec des chaînes citées:

repeat 3 0 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.

repeat -1 .5 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:14:14, Hello Guy.
Now: 15:14:14, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:18, Hello Guy.
Now: 15:14:18, Hello Guy.
^C

En fonction de la granularité et de la durée de la commande soumise ...

Dans les noyaux Linux récents, il existe un fichier procfile /proc/timer_listcontenant des informations de temps en nanosecondes.

Si vous voulez exécuter une commande exactement une fois par seconde, votre commande doit se terminer en moins d'une seconde! Et à partir de là, il sleepne reste que le reste de la seconde actuelle.

Si le délai est plus important et que votre commande ne nécessite pas beaucoup de temps, vous pouvez:

command=(echo 'Hello world.')
delay=10
while :;do
    printf -v now "%(%s)T" -1
    read -t $(( delay-(now%delay) )) foo
    ${command[@]}
  done.

Mais si votre objectif est d’obtenir une granularité plus fine, vous devez:

Utilisez les informations en nanosecondes pour attendre le début d'une seconde ...

Pour cela, j'ai écrit une petite fonction bash:

# bash source file for nano wait-until-next-second

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
waitNextSecondHires() {
    local nsnow nsslp
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsecs*}
    nsnow=$((${nsnow##* }+TIMER_LIST_OFFSET))
    nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
    read -t .${nsslp:1} foo
}

Après les avoir achetés, vous pourriez:

command=(echo 'Hello world.')
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

exécuter ${command[@]}directement en ligne de commande, que comparer à

command=(eval "echo 'Hello world.';sleep .3")
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

cela doit donner exactement le même résultat.

Engage la fonction " repeat" à la demande:

Vous pourriez trouver ceci:

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ

repeat_hires () {
    local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep repeat_count
    read -t .0001 repeat_foo
    if [ $? = 1 ] ;then
        repeat_sleep() { sleep $1 ;}
    else
        repeat_sleep() { read -t $1 repeat_foo; }
    fi
    shift 2
    printf -v repeat_delay "%.9f" $repeat_delay
    repeat_delay=${repeat_delay//.}
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsec*}
    started=${nsnow##* }
    while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times)) && ((10#$repeat_delay)) && {
            read -N$TIMER_LIST_READ nsnow </proc/timer_list
            nsnow=${nsnow%% nsec*}
            nsnow=${nsnow##* }
            (( (nsnow - started) / 10#$repeat_delay - repeat_count++ )) &&
                printf >&2 "WARNING: Command '%s' too long for %f delay.\n" \
                           "${*}" ${repeat_delay:0:${#repeat_delay}-9
                           }.${repeat_delay:${#repeat_delay}-9}
            printf -v sleep "%010d" $((
                10#$repeat_delay - ( ( nsnow - started ) % 10#$repeat_delay ) ))
            repeat_sleep ${sleep:0:${#sleep}-9}.${sleep:${#sleep}-9}
        }
    done
}

Alors essayez-le:

time repeat_hires 21 .05 sh -c 'date +%s.%N;sleep .01'
1480867565.152022457
1480867565.201249108
1480867565.251333284
1480867565.301224905
1480867565.351236725
1480867565.400930482
1480867565.451207075
1480867565.501212329
1480867565.550927738
1480867565.601199721
1480867565.651500618
1480867565.700889792
1480867565.750963074
1480867565.800987954
1480867565.853671458
1480867565.901232296
1480867565.951171898
1480867566.000917199
1480867566.050942638
1480867566.101171249
1480867566.150913407

real    0m1.013s
user    0m0.000s
sys     0m0.016s

time repeat_hires 3 .05 sh -c 'date +%s.%N;sleep .05'
1480867635.380561067
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.486503367
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.582332617

real    0m0.257s
user    0m0.000s
sys     0m0.004s

read -test intégré , alors que sleepnon. Cela pourrait avoir un effet de bordure (c.-à-d. Que vous appuyez sur [touche: revenir] interrompre le sommeil tranquillement ), mais cela pourrait être traité par des tests$foo
F. Hauri

Réponse très impressionnante
gena2x

4

Tu veux essayer ça (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }

Je ne suis pas au chaud, donc les améliorations sont les bienvenues. Et je ne semble pas avoir de commande "répéter" dans ma distribution.

2
repeatest spécifique à csh et tcsh.
Keith Thompson

2
@KeithThompson: et zsh
Thor

4

Perl

#!/usr/bin/env perl
# First argument is number of seconds to sleep between repeats, remaining
# arguments give the command to repeat forever.

$sleep = shift;
$cmd = join(' ', @ARGV);

while(1) {
  system($cmd);
  sleep($sleep); 
}

4

Bash et certains de ses shells apparentés ont la (( ... ))notation pratique dans laquelle les expressions arithmétiques peuvent être évaluées.

Donc, pour répondre à votre troisième défi, où le nombre de répétitions et le délai entre chaque répétition doivent être configurables, voici une façon de le faire:

repeat=10
delay=1

i=0
while (( i++ < repeat )); do
  echo Repetition $i
  sleep $delay
done

Cette réponse souffre également de la dérive de synchronisation couverte par la réponse de Keith .


C'était le meilleur.
Ahmad Awais

3

Comme mentionné par gbrandt, si la watchcommande est disponible, utilisez-la. Cependant, certains systèmes Unix ne l’ont pas installé par défaut (du moins, ils ne le sont pas où je travaille).

Voici une autre solution avec une syntaxe et une sortie légèrement différentes (fonctionne sous BASH et SH):

while [ 1 ] ; do
    <cmd>
    sleep <x>
    echo ">>>>>>>>>>>>>" `date` ">>>>>>>>>>>>>>"
done

Edit: j'ai enlevé des "." dans la dernière déclaration d'écho ... le reste de mes jours Perl;)


3
while [ 1 ]ne fonctionne que parce que 1 est traité comme une chaîne, tout comme while [ -n 1 ]. while [ 0 ]ou while [ jeff ]ferait la même chose. while trueest beaucoup plus logique.
Mikel

3

Si votre intention n'est pas d'afficher un message sur votre écran et si vous pouviez vous permettre de répéter le travail en minutes, crontab serait peut-être votre meilleur outil. Par exemple, si vous souhaitez exécuter votre commande toutes les minutes, vous écrirez quelque chose comme ceci dans votre crontabfichier:

* * * * * my_precious_command

S'il vous plaît consulter le tutoriel pour plus d'exemple. En outre, vous pouvez facilement définir les minutages à l’aide de Crontab Code Generator .


3

Et si on avait les deux?

Voici l’idée: avec seulement "intervalle", cela se répète pour toujours. Avec "intervalle" et "fois", il répète ce nombre de fois, séparé par "intervalle".

L'usage :

$ loop [interval [times]] command

Donc, l'algorithme sera:

  • Élément de liste
  • si $ 1 ne contient que des chiffres, c'est l'intervalle (2 par défaut)
  • si $ 2 ne contient que des chiffres, c'est le nombre de fois (infini par défaut)
  • boucle while avec ces paramètres
    • sommeil "intervalle"
    • si un nombre de fois donné a été donné, décrémente un var jusqu'à ce qu'il soit atteint

Par conséquent :

loop() {
    local i=2 t=1 cond

    [ -z ${1//[0-9]/} ] && i=$1 && shift
    [ -z ${1//[0-9]/} ] && t=$1 && shift && cond=1
    while [ $t -gt 0 ]; do 
        sleep $i
        [ $cond ] && : $[--t]
        $@
    done
}

Remarque: cela ne fonctionne pas avec les flotteurs, malgré les sleepaccepte.
Baronsed

2

J'ai fini par créer une variante sur la réponse de swalog. Avec le sien, vous deviez attendre X secondes pour la première itération, je cours aussi au premier plan alors ..

./foo.sh;while sleep 1; do (./foo.sh) ; done

1

Rapide, sale et probablement dangereux pour démarrer, mais si vous êtes aventureux et savez ce que vous faites, mettez cela en repeat.shet chmod 755il,

while true
do 
    eval $1 
    sleep $2 
done

Invoquer avec ./repeat.sh <command> <interval>

Mon sens spidey dit que c'est probablement une façon perverse de faire cela. Mon sens spidey est-il correct?


8
Eval !? Pourquoi? sleep $1; shift; "$@"ou similaire serait beaucoup mieux.
Mikel

Je ne sais pas pourquoi c'est -2. eval est peut-être exagéré ... sauf lorsque vous en avez besoin. Eval vous permet d’obtenir des éléments tels que la redirection sur la ligne de commande.
Johan

1

Vous pouvez exécuter un script à partir d'init (en ajoutant une ligne à / etc / inittab). Ce script doit exécuter votre commande, patientez pendant le temps que vous souhaitez attendre jusqu'à ce qu'il soit exécuté à nouveau et quittez-le. Init lancera votre script à nouveau après la sortie.


1

Un moyen facile de répéter un travail à partir de crontab avec un intervalle inférieur à une minute (exemple de 20 secondes):

crontab: * * * * * script.sh

script.sh:

#!/bin/bash
>>type your commands here.

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.

1

Vous pouvez obtenir le pouvoir de l'interpréteur python de shell.

python3 -c "import time, os
while True:
    os.system('your command')
    time.sleep(5)

remplacez-en cinq en tout temps (sec) que vous aimez.

Attention: le retour utilise SHIFT + ENTER pour renvoyer une entrée en shell. Et n'oubliez pas de taper 4 SPACE indent dans chaque ligne de la boucle while.


Notez que pythonl » os.system()exécute un shell pour interpréter la ligne de commande, afin d' invoquer pythonpour lancer des obus dans une boucle pour exécuter une commande est périodiquement un peu exagéré. Vous pourriez aussi bien tout faire dans la coquille.
Stéphane Chazelas

Je suis d'accord avec toi. Cependant, il y a un sommeil de 5 secondes comme décrit dans chaque boucle. Ainsi, le coût supplémentaire du démarrage d'un nouveau shell peut être ignoré. Si ce coût rend la période plus longue que prévu, vous pouvez réduire le temps de sommeil en fonction des besoins.
pah8J

0

Cette solution fonctionne sous MacOSX 10.7. Cela fonctionne à merveille.

bash -c 'while [ 0 ]; do \
      echo "I will not automate this punishment in absurdum."; done'

Dans mon cas

bash -c 'while [ 0 ]; do ls; done'

ou

bash -c 'while [ 0 ]; do mv "Desktop/* Documents/Cleanup"; done'

nettoyer mon bureau en permanence.


1
Voulez-vous dire avoir une sleepcommande là-bas?
Keith Thompson

4
while [ 0 ]est un moyen étrange d'écrire une boucle infinie; cela fonctionne car la testcommande (également connue sous le nom [) traite une chaîne non vide comme étant vraie. while : ; do ... ; doneest plus idiomatique, ou si vous préférez, vous pouvez utiliser while true ; do ... ; done.
Keith Thompson

0

Vous pouvez source cette fonction récursive:

#!/bin/bash
ininterval () {
    delay=$1
    shift
    $*
    sleep $delay
    ininterval $delay $*
}

ou ajouter un:

ininterval $*

et appelez le script.


0

Pour exécuter de manière répétée une commande dans une fenêtre de console, je lance généralement quelque chose comme ceci:

while true; do (run command here); done

Cela fonctionne également pour plusieurs commandes, par exemple pour afficher une horloge continuellement mise à jour dans une fenêtre de console:

while true; do clear; date; sleep 1; done


Pourquoi le vote négatif? Vous pouvez voir que cette solution fonctionne en copiant et en collant l'exemple dans une fenêtre de terminal.
Thomas Bratt

Je pense que vous avez voté parce que c'est un peu dangereux et consomme de la CPU.
ojblass

0
#! /bin/sh

# Run all programs in a directory in parallel
# Usage: run-parallel directory delay
# Copyright 2013 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

if [ $# -eq 0 ]
then
   echo
   echo "run-parallel by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel" 
   echo "or to rerun them every X seconds for one minute."
   echo "Think of this program as cron with seconds resolution."
   echo
   echo "Usage: run-parallel [directory] [delay]"
   echo
   echo "Examples:"
   echo "   run-parallel /etc/cron.20sec 20"
   echo "   run-parallel 20"
   echo "   # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute."
   echo 
   echo "If delay parameter is missing it runs everything once and exits."
   echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed."
   echo
   echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30'
   echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
   echo
   exit
fi

# If "cronsec" is passed as a parameter then run all the delays in parallel

if [ $1 = cronsec ]
then
   $0 2 &
   $0 3 &
   $0 4 &
   $0 5 &
   $0 6 &
   $0 10 &
   $0 12 &
   $0 15 &
   $0 20 &
   $0 30 &
   exit
fi

# Set the directory to first prameter and delay to second parameter

dir=$1
delay=$2

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]]
then
   dir="/etc/cron.$1sec"
   delay=$1
fi

# Exit if directory doesn't exist or has no files

if [ ! "$(ls -A $dir/)" ]
then
   exit
fi

# Sleep if both $delay and $counter are set

if [ ! -z $delay ] && [ ! -z $counter ]
then
   sleep $delay
fi

# Set counter to 0 if not set

if [ -z $counter ]
then
   counter=0
fi

# Run all the programs in the directory in parallel
# Use of timeout ensures that the processes are killed if they run too long

for program in $dir/* ; do
   if [ -x $program ] 
   then
      if [ "0$delay" -gt 1 ] 
      then
         timeout $delay $program &> /dev/null &
      else
         $program &> /dev/null &
      fi
   fi
done

# If delay not set then we're done

if [ -z $delay ]
then
   exit
fi

# Add delay to counter

counter=$(( $counter + $delay ))

# If minute is not up - call self recursively

if [ $counter -lt 60 ]
then
   . $0 $dir $delay &
fi

# Otherwise we're done

0

Avec zshet une sleepimplémentation qui accepte les arguments en virgule flottante:

typeset -F SECONDS=0 n=0
repeat 100 {cmd; sleep $(((n+=3) - SECONDS))}

Ou pour forever:

for ((;;)) {cmd; sleep $(((n+=3) - SECONDS))}

Si votre sleepne supporte pas les float, vous pouvez toujours le redéfinir en tant que wrapper autour zshde zselect:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))

-1

... Je me demande combien de solutions compliquées peuvent être créées pour résoudre ce problème.

Cela peut être si facile ...

open /etc/crontab 

mettez-y 1 ligne suivante à la fin du fichier comme:

*/NumberOfSeconds * * * * user /path/to/file.sh

Si vous voulez exécuter quelque chose toutes les 1 secondes, mettez-y simplement:

*/60 * * * * root /path/to/file.sh 

where that file.sh could be chmod 750 /path/to/file.sh

et à l'intérieur de ce fichier.sh devrait être:

#!/bin/bash 
#What does it do
#What is it runned by

your code or commands

et c'est tout!

PRENDRE PLAISIR!


6
Ce n'est pas correct * / 60 sera exécuté toutes les 60 minutes et * / 5, par exemple, toutes les 5 minutes.
Mika

1
Les secondes ne sont pas autorisées sur crontab
GAD3R

testez vos affaires avant de répondre avec une mauvaise merde.
Sjas

-1
until ! sleep 60; do echo $(date); command; command; command; done

travaille pour moi.

  1. Je n'en ai pas besoin exactement toutes les 60 secondes
  2. "watch" ne regarde pas les commandes sur tous les systèmes
  3. "watch" ne peut prendre qu'une seule commande (ou un fichier de script)
  4. mettre le sommeil dans l'état au lieu du corps rend la boucle plus facilement interruptible (donc une réclamation dans une réponse à une question similaire sur stackoverflow. Malheureusement, je ne le trouve pas pour le moment)
  5. Je veux l'exécuter une fois avant le délai, donc je l'utilise jusqu'au lieu de

Je viens de remarquer que "jusqu'à ce que" apparemment exécute le sommeil avant la première itération aussi. y a-t-il un moyen de mettre la condition à la fin de la boucle dans bash?
Tite
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.