J'ai un script shell qui lit à partir d'une entrée standard . Dans de rares cas, personne ne sera prêt à apporter une contribution et le script doit expirer . En cas de dépassement de délai, le script doit exécuter du code de nettoyage. Quelle est la meilleure façon de faire ça?
Ce script doit être très portable , y compris pour les systèmes unix du XXe siècle sans compilateur C et pour les périphériques intégrés exécutant busybox. Perl, bash, tous les langages compilés et même le POSIX.2 complet ne peuvent pas être fiables. En particulier, $PPID
, read -t
et parfaitement pièges conformes POSIX ne sont pas disponibles. L'écriture dans un fichier temporaire est également exclue; le script peut s'exécuter même si tous les systèmes de fichiers sont montés en lecture seule.
Juste pour rendre les choses plus difficiles, je veux aussi que le script soit raisonnablement rapide quand il n’expire pas. En particulier, j'utilise également le script sous Windows (principalement sous Cygwin), où fork et exec sont particulièrement faibles, je souhaite donc limiter leur utilisation au minimum.
En un mot, j'ai
trap cleanup 1 2 3 15
foo=`cat`
et je veux ajouter un délai d'attente. Je ne peux pas remplacer cat
par la fonction read
intégrée. En cas de dépassement de délai, je veux exécuter la cleanup
fonction.
Arrière-plan: ce script consiste à deviner l'encodage du terminal en imprimant des caractères de 8 bits et en comparant la position du curseur avant et après. Le début du script vérifie que stdout est connecté à un terminal pris en charge, mais parfois l'environnement est menteur (par exemple, des plink
ensembles TERM=xterm
même s'il est appelé avecTERM=dumb
). La partie pertinente du script ressemble à ceci:
text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code
stty eol 0 eof n -echo # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position
initial_report=`tr -dc \;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc \;0123456789`
cleanup
# Compute and return initial_x - final_x
Comment puis-je modifier le script pour que si tr
aucune entrée ne soit lue après 2 secondes, elle soit supprimée et que le script exécute la cleanup
fonction?
Réponses:
Et ça:
C'est-à-dire: exécutez la commande produisant une sortie et
sleep
dans le même groupe de processus, un groupe de processus juste pour eux. Quelle que soit la commande renvoyée, le groupe de processus entier est tué en premier.Quelqu'un pourrait-il se demander: oui, le tuyau n'est pas utilisé; il est contourné en utilisant les redirections. Son seul objectif est que le shell exécute les deux processus dans le même groupe de processus.
Comme Gilles l'a souligné dans son commentaire, cela ne fonctionnera pas dans un script shell car le processus de script serait tué en même temps que les deux sous-processus.
Une façon¹ de forcer une commande à s'exécuter dans un groupe de processus distinct consiste à démarrer un nouveau shell interactif:
Mais il pourrait y avoir des réserves à ce sujet (par exemple, quand stdin n’est pas un tty?). La redirection stderr est là pour supprimer le message "Terminated" lorsque le shell interactif est tué.
Testé avec
zsh
,bash
etdash
. Mais qu'en est-il des vieux?B98 suggère la modification suivante, fonctionnant sous Mac OS X, avec GNU bash 3.2.57 ou sous Linux avec tiret:
-
1. autre que
setsid
ce qui semble être non standard.la source
kill 0
finit également par tuer l'appelant du script. Existe-t-il un moyen portable de forcer le pipeline dans son propre groupe de processus?setprgp()
passer desetsid
pour l'instant :-(Vous êtes déjà au piège de 15 (la version numérique de
SIGTERM
, qui est ce quikill
envoie sauf indication contraire), alors vous devriez déjà être prêt à partir. Cela dit, si vous examinez les versions antérieures à POSIX, sachez que les fonctions du shell peuvent également ne pas exister (elles proviennent du shell de System V).la source
cat
de quitter. J'ai déjà expérimenté un peu mais je n'ai rien trouvé qui me satisfasse jusqu'à présent.$!
, je pense que certaines de ces machines que j'utilise rarement n'ont pas de contrôle du travail, sont-elles$!
universellement disponibles?bash
choses vraiment hideuses et spécifiques que j'ai faites une fois impliquant l'abus de-o monitor
. Je pensais en termes de coquilles vraiment anciennes quand j'ai écrit cela (cela a fonctionné dans la v7). Cela dit, je pense que vous pouvez faire l'une des deux choses suivantes: (1) fond "quels que soient" etwait $!
, ou (2) aussi envoyerSIGCLD
/SIGCHLD
... mais sur des machines assez anciennes, la dernière est inexistante ou non portable (la première est System III / V, ce dernier BSD et V7 n’ont ni l’un ni l’autre).$!
remonte au moins à la V7 et est certainement antérieur à unsh
type qui connaissait le contrôle des tâches (en fait, pendant longtemps/bin/sh
, BSD ne contrôlait pas les tâches; il fallait courircsh
pour l'obtenir, mais$!
il y en avait )$!
.Bien que coretuils à partir de la version 7.0 inclue une commande de dépassement de temps, vous avez mentionné certains environnements qui ne l’auraient pas. Heureusement, pixelbeat.org a écrit un script de temporisation
sh
.Je l'ai déjà utilisé à plusieurs reprises et cela fonctionne très bien.
http://www.pixelbeat.org/scripts/timeout ( Remarque: le script ci-dessous a été légèrement modifié par rapport à celui de pixelbeat.org, voir les commentaires sous cette réponse.)
la source
</dev/stdin
est un no-op.</dev/tty
lui permettrait de lire depuis le terminal, ce qui est suffisant pour mon cas d'utilisation.Qu'en est-il (ab) utiliser NC pour cette
Comme;
Ou réunis dans une seule ligne de commande;
La dernière version, bien que semblant étrange, fonctionne réellement lorsque je la teste sur un système linux - ma meilleure hypothèse est que cela devrait fonctionner sur n’importe quel système. Sinon, une variation de la redirection de sortie pourrait résoudre le problème de la portabilité. L'avantage ici est qu'aucun processus en arrière-plan n'est impliqué.
la source
nc
sur de vieux boîtiers Unix, ni sur beaucoup de Linux embarqué.Une autre façon d'exécuter le pipeline dans son propre groupe de processus consiste à s'exécuter
sh -c '....'
dans un pseudo-terminal à l'aide de lascript
commande (qui applique implicitement lasetsid
fonction).la source
script
sur de vieux boîtiers Unix, ni sur beaucoup de Linux embarqué.La réponse dans https://unix.stackexchange.com/a/18711 est très agréable.
Je voulais obtenir un résultat similaire, mais sans avoir à appeler explicitement à nouveau le shell car je voulais invoquer des fonctions de shell existantes.
En utilisant bash, il est possible de faire ce qui suit:
Supposons donc que j'ai déjà une fonction shell
f
:En exécutant ceci, je vois:
la source
la source