Pourquoi une boucle while s'arrête-t-elle après avoir été suspendue?

10

Pourquoi est-ce qu'en utilisant bash et en suspendant une boucle while, la boucle s'arrête après avoir été reprise? Petit exemple ci-dessous.

$ while true; do echo .; sleep 1; done
.
.
^Z
[1]+  Stopped                 sleep 1
$ fg
sleep 1
$

Je connais les signaux et je suppose que cela peut être le comportement naturel de bash ici, mais j'aimerais mieux comprendre pourquoi il se comporte de cette manière particulière.

bkzland
la source
car il doit gérer une interruption et doit refléter cela avec précision lors $?du retour, et ce truen'est pas le cas alors true. Probablement. je pense.
mikeserv
1
J'espère que ce commentaire ne sera pas signalé, mais je répondrai à votre question par une autre question, de style koan Unix: "Pourquoi un élève arrête-t-il de se battre sur le terrain de jeu après avoir été suspendu?" La réponse étant, car il n'est plus au terrain de jeu où il a la possibilité de commencer des combats. Ainsi, le comportement en question a simplement été stoppé.
rubynorails
Vous arrêtez la commande, la boucle est rompue. Ensuite, vous reprenez la commande single sleep 1, pas la boucle.
123
1
Pertinent
123

Réponses:

10

Cela ressemble à un bogue dans plusieurs shells, il fonctionne comme prévu avec ksh93 et zsh .

Contexte:

La plupart des shells semblent exécuter la boucle while à l'intérieur du shell principal et

Bourne Shell suspend le shell entier si vous tapez ^ Z avec un shell sans connexion

bash suspend uniquement le sleeppuis quitte la boucle while pour imprimer une nouvelle invite shell

le tiret rend cette commande indéfendable

Avec ksh93 , les choses fonctionnent très différemment:

ksh93 fait de même, tandis que la commande est lancée la première fois, mais comme sleepc'est le cas dans ksh93, ksh93 a un gestionnaire qui fait que la boucle while déborde du shell principal puis se suspend au moment où vous tapez ^ Z.

Si vous tapez ksh93 ultérieurement fg, l'enfant dérivé qui exécute toujours la boucle se poursuit.

Vous voyez la principale différence lorsque vous comparez les messages de jobcontrol de bash et ksh93:

rapports bash :

[1]+ Stopped sleep 1

mais ksh93 rapporte:

^Z[1] + Stopped while true; do echo .; sleep 1; done

zsh se comporte comme ksh93

Avec les deux shells, vous avez un seul processus (le shell principal) tant que vous ne tapez pas ^ Z, et deux processus de shell après avoir tapé ^ Z.

schily
la source
ne dashfinit pas réellement par gérer le signal lorsque la boucle se termine? dans [d]?ashle code source, il y a toutes ces macros pour INTON et INTOFF dispersées à travers, et généralement les signaux reçus alors que dans un état INTOFF sont vraiment traités à (ou autour) INTON . de toute façon, je ne suis curieux que parce que je pense que vous savez mieux - c'est une excellente réponse. Merci.
mikeserv
J'utilise rarement dash et je l'ai récemment récupéré et compilé pour des comparaisons de performances avec bash, ksh93 et ​​mon Bourne Shell. En faisant ces tests, j'ai découvert que le tiret semble principalement être rapide car il n'inclut pas la prise en charge des caractères multi-octets. Un seul sleep 100peut être suspendu et repris dash, il semble donc qu'il dashconnaisse les problèmes de cette commande et désactive sélectivement le contrôle des travaux.
schily
donc, dans vos tests, vous avez pu égaler dashles performances des autres shells en abandonnant le traitement multi-octets? et oui, dashprend en charge le contrôle des travaux, mais la norme dit qu'un shell interactif doit ignorer TSTP, et l'exécution d'une boucle while dans le shell actuel sur un terminal interactif n'est pas moins un shell interactif que tout autre.
mikeserv
Je n'ai pas testé cela exactement, mais j'ai pu voir que si le tableau de bord consomme plus de temps CPU système que ksh93 ou le Bourne Shell (principalement parce qu'il émet plus d'appels fork ()), il utilise moins de temps CPU utilisateur et cela se traduit par un total similaire Temps CPU comparé à ma version du Bourne Shell. En essayant de réduire le temps processeur de l'utilisateur dans le Bourne Shell, je sais que la plupart de ce temps est consacré à des conversions multi-octets.
schily
4

J'ai écrit à l'un des co-auteurs de Bash à ce sujet, et voici sa réponse:

Ce n'est pas vraiment un bug, mais c'est certainement une mise en garde.

L'idée ici est que vous suspendez les processus, qui sont une unité de granularité différente des commandes shell. Lorsqu'un processus est suspendu, il revient au shell (avec un état différent de zéro, ce qui a des conséquences lorsque vous, par exemple, arrêtez un processus qui est le test de boucle), qui a le choix: il peut sortir ou continuer la boucle , laissant le processus arrêté derrière. Bash choisit - et a toujours choisi - de rompre les boucles lorsqu'un travail est arrêté. Poursuivre la boucle est rarement ce que vous voulez.

Certains autres shells font des choses comme fork une copie du shell quand un processus est suspendu à cause de SIGTSTP et arrêtent ce processus. Bash n'a jamais fait cela - cela semble plus compliqué que les avantages sociaux - mais si quelqu'un veut soumettre ce code en tant que correctif, j'essaierais d'incorporer les modifications.

Donc, si quelqu'un veut soumettre un patch, utilisez les adresses e-mail trouvées dans les pages de manuel.

en avant
la source