Équivalent POSIX pour la temporisation GNU?

14

La timeoutcommande GNU coreutils est extrêmement pratique pour certaines situations de script, permettant d'utiliser la sortie d'une commande si elle est rapide à exécuter et de la sauter si elle prendrait trop de temps.

Comment puis-je approximer le comportement de base de l' timeoututilisation uniquement des utilitaires spécifiés POSIX?


(Je pense qu'il peut impliquer une combinaison de wait, sleep, killet qui sait quoi d' autre, mais peut - être que je manque une approche plus facile.)

Caractère générique
la source
3
Voir Délai d'expiration dans un script shell , mais je ne considère pas cela comme un doublon car j'ai demandé la portabilité vers les systèmes pré-POSIX et j'avais l'exigence de préserver stdin et stdout, ce que certaines solutions impliquant des processus d'arrière-plan excluent.
Gilles 'SO- arrête d'être méchant'
command & pid=$! ; sleep 5 && kill $pid
Pandya
1
@Pandya n'introduit-il pas une légère condition de concurrence, dans le cas où s'il se commandtermine rapidement, il y a peu de chances d' pidêtre réutilisé par un autre processus démarrant avant l' killexécution de la commande? Je ne voudrais pas cela dans le code de production ....
Wildcard
Pouvez-vous expliquer davantage le problème sous-jacent que vous essayez de résoudre? Pourquoi ne pas simplement compiler le timeoutprogramme et l'utiliser?
James Youngman
2
@JamesYoungman, je suppose que vous n'êtes pas très familier avec l' écriture de scripts pour la portabilité . Demander une manière de faire quelque chose conforme à POSIX (ou spécifié par POSIX) implique qu'elle est destinée à être portable. La compilation de code dans un fichier binaire est absolument pas portable, et, en fonction de vos politiques de sécurité de l' entreprise, vous ne pouvez pas avoir un compilateur installé du tout sur un serveur de production.
Wildcard

Réponses:

2

Mon approche serait celle-ci:

  • Exécuter la commande comme processus d'arrière-plan 1

  • Exécutez le "temporisateur de surveillance" comme processus d'arrière-plan 2

  • Configurer un gestionnaire pour intercepter un signal de terminaison dans le shell parent

  • Attendez la fin des deux processus. Le processus qui se termine en premier, envoie le signal de terminaison au parent.

  • Le gestionnaire de pièges du parent tue les deux processus d'arrière-plan via le contrôle des tâches (l'un d'eux a déjà pris fin par définition, mais cette suppression sera une opération sans danger car nous n'utilisons pas de PID, voir ci-dessous)

J'ai essayé de contourner la condition de concurrence possible abordée dans les commentaires en utilisant les ID de contrôle des tâches du shell (qui seraient sans ambiguïté dans cette instance de shell) pour identifier les processus d'arrière-plan à tuer, au lieu des PID système.

#!/bin/sh

TIMEOUT=$1
COMMAND='sleep 5'

function cleanup {
    echo "SIGTERM trap"
    kill %1 %2
}

trap cleanup SIGTERM

($COMMAND; echo "Command completed"; kill $$) &
(sleep $TIMEOUT; echo "Timeout expired"; kill $$) &

wait
echo "End of execution"

Résultat pour TIMEOUT=10(la commande se termine avant le chien de garde):

$ ./timeout.sh 10
Command completed
SIGTERM trap
End of execution

Résultat pour TIMEOUT=1(le chien de garde se termine avant la commande):

$ ./timeout.sh 1
Timeout expired
SIGTERM trap
End of execution

Résultat pour TIMEOUT=5(le chien de garde et la commande se terminent "presque" simultanément):

./tst.sh 5
Timeout expired
Command completed
SIGTERM trap
End of execution
Guido
la source
Ce n'est pas conforme à POSIX car (a) la définition de la fonction devrait être cleanup() { ...; }et (b) le contrôle des travaux est une fonctionnalité bash non spécifiée par POSIX (bien que ksh et d'autres l'aient également). Beau script, cependant!
Wildcard
Vous pouvez également faire mieux avec timeout="$1"; shiftet (exec "$@"; echo "Command completed"; kill $$)plutôt que de réécrire la liste des arguments.
Wildcard