Exécuter la commande toutes les X secondes

10

Je souhaite exécuter une commande toutes les 10 secondes et l'exécuter en arrière-plan (éliminant ainsi watch?). Toutes les réponses montrent quelque chose comme ce qui suit, mais cela s'exécutera de 11 à 14 secondes. Comment cela peut-il être accompli?

while true; do
    # perform command that takes between 1 and 4 seconds
    sleep 10
done
user1032531
la source
peut-être avec des signaux en temps réel. et une bonne manipulation des secondes intercalaires. bonne chance.
mikeserv
@mikeserv Alors, ne vous embêtez pas à essayer? Je me fiche de la précision en millisecondes, disons juste une demi-seconde.
user1032531
Qu'est-ce que vous essayez d'accomplir? Je trouve qu'une tâche qui doit s'exécuter avec ce type de fréquence est généralement une solution de contournement pour un autre problème.
Sobrique
@Sobrique Exactement. Je fais une preuve de concept d'un enregistreur de données de sondage. En réalité, il sera programmé dans une autre langue, mais cela fonctionne pour l'instant.
user1032531

Réponses:

16

Que diriez-vous:

( # In a subshell, for isolation, protecting $!
  while true; do
    perform-command & # in the background
    sleep 10 ;
    ### If you want to wait for a perform-command
    ### that happens to run for more than ten seconds,
    ### uncomment the following line:
    # wait $! ;
    ### If you prefer to kill a perform-command
    ### that happens to run for more than ten seconds,
    ### uncomment the following line instead:
    # kill $! ;
    ### (If you prefer to ignore it, uncomment neither.)
  done
)

ETA: Avec tous ces commentaires, alternatives et le sous-shell pour une protection supplémentaire, cela semble beaucoup plus compliqué qu'il ne l'avait commencé. Donc, à titre de comparaison, voici à quoi cela ressemblait avant de commencer à m'inquiéter waitou kill, avec leur $!besoin d'isolement:

while true; do perform-command & sleep 10 ; done

Le reste est vraiment juste pour quand vous en avez besoin.

Le Sidhekin
la source
Pouvez-vous expliquer le raisonnement derrière votre vidage de code?
Ismael Miguel du
2
Aussi, pour citer le shell pour lequel votre code fonctionne (comme la réponse de mikeserv) afin que les gens ne se trompent pas quand un autre shell se comporte différemment :)
ash
@IsmaelMiguel J'ai pensé que les commentaires expliqueraient le raisonnement. Je ne sais pas ce qui n'est pas clair.
Le Sidhekin
1
Le manuel bash dit $!: "S'étend à l'ID de processus du travail le plus récemment placé en arrière-plan", et le sleepn'est pas placé en arrière-plan, donc non, il ne le fera pas. :)
Le Sidhekin
1
sensationnel. mal sur les deux points. Quand est-ce arrivé?
mikeserv du
9

Vous pouvez faire quelque chose comme ce qui suit dans bash, zshou ksh:

SECONDS=0
while   command
do      sleep "$((10-(SECONDS%10)))"
done

Voici ce que bashdit le manuel $SECONDS:

$SECONDS

  • Chaque fois que ce paramètre est référencé, le nombre de secondes écoulées depuis l'appel du shell est renvoyé. Si une valeur est affectée à $SECONDS, la valeur renvoyée lors des références suivantes est le nombre de secondes écoulées depuis l'affectation plus la valeur affectée. Si $SECONDSc'est le cas unset, il perd ses propriétés spéciales, même s'il est réinitialisé par la suite.

Voici un exemple de travail:

(   SECONDS=0
    while   sleep   "$((RANDOM%10))"
    do      sleep   "$((10-(SECONDS%10)))"
            echo    "$SECONDS"
    done
)

10
20
30
40
50
60
70
80
90
100
mikeserv
la source
2
Pour éviter de réinitialiser SECONDSfaire sleep "$((10-(($SECONDS-$start_time)%10)))". Vous devrez faire start_time=$SECONDSavant la boucle.
ctrl-alt-delor
@richard - pourquoi l'éviter?
mikeserv
parce qu'un jour vous l'aurez dans un script que vous appelez à partir d'une telle boucle. Et vous passerez toute la journée à vous demander pourquoi cela ne fonctionne pas correctement. Bref, il ne s'emboîtera pas, si vous le réinitialisez SECONDS. La génération d'un sous-shell comme dans votre dernière édition devrait également éviter ce problème.
ctrl-alt-delor
1
@richard - étant donné que la question concernait une while trueboucle, je ne pensais pas qu'un état shell devait y survivre. Envelopper une boucle comme ça dans son propre contexte est généralement la meilleure pratique, de toute façon. Dans une situation comme celle-là, je serais plus préoccupé de le faire pour traple plaisir que pour $SECONDSle plaisir.
mikeserv
Merci Mike, je pensais faire quelque chose de similaire (mais je ne connaissais pas encore les détails). Mais la solution de Sidhekin semble faire la même chose, non? Je ne suis pas qualifié pour juger de la meilleure réponse, mais je me sentais obligé de choisir quelque chose.
user1032531
3

BASH uniquement - Vous pouvez également calculer le temps passé par votre commande et soustraire de 10:

TIMEFORMAT=$'%0R'
while true; do
    T=$({ time command; } 2>&1)
    sleep $(( 10-T ))

De l'homme BASH:

TIMEFORMAT La valeur de ce paramètre est utilisée comme une chaîne de format spécifiant comment les informations de synchronisation pour les pipelines précédés du mot réservé doivent être affichées. Le caractère% introduit une séquence d'échappement qui est étendue à une valeur de temps ou à d'autres informations. Les séquences d'échappement et leurs significations sont les suivantes; les accolades désignent des portions facultatives.
%% Un littéral%.
% [p] [l] R Le temps écoulé en secondes.
% [p] [l] U Le nombre de secondes CPU passées en mode utilisateur.
% [p] [l] S Le nombre de secondes CPU passées en mode système.
% P Le pourcentage CPU, calculé comme (% U +% S) /% R.

Le p facultatif est un chiffre spécifiant la précision, le nombre de chiffres fractionnaires après un point décimal. Une valeur de 0 ne génère aucun point décimal ni aucune fraction.

Dani_l
la source