Appuyez sur n'importe quelle touche pour suspendre le script shell, appuyez à nouveau pour reprendre

10

J'ai écrit un script shell pour tester une API qui copie des fichiers et fait écho à sa progression après chacun.

Il y a un sommeil de deux secondes entre chaque copie, donc je voudrais ajouter la possibilité d'appuyer sur n'importe quelle touche pour suspendre le script pour permettre des tests plus approfondis. Appuyez ensuite sur n'importe quelle touche pour reprendre.

Comment puis-je ajouter cela en aussi peu de lignes que possible?

blarg
la source

Réponses:

12

Vous n'avez pas besoin d'ajouter quelque chose à votre script. Le shell permet une telle fonctionnalité.

  • Démarrez votre script dans un terminal.
  • Pendant l'exécution et le blocage de l'utilisation du terminal ctrl- z. Le terminal est à nouveau libéré et vous voyez un message indiquant que le processus est arrêté. (Il est maintenant à l'état de porc T, arrêté)
  • Maintenant, fais ce que tu veux. Vous pouvez également démarrer d'autres processus / scripts et les arrêter avec ctrl- z.
  • Saisissez jobsle terminal ou répertoriez tous les travaux interrompus.
  • Pour laisser votre script continuer, tapez fg(premier plan). Il reprend le travail dans le groupe de processus de premier plan et les travaux continuent de s'exécuter.

Voir un exemple:

root@host:~$ sleep 10 # sleep for 10 seconds
^Z
[1]+  Stopped                 sleep 10
root@host:~$ jobs # list all stopped jobs
[1]+  Stopped                 sleep 10
root@host:~$ fg # continue the job
sleep 10
root@host:~$ # job has finished
le chaos
la source
comme vous l'avez dit, si je cours sleep 10; notify-send helloet appuie sur CTRL + Z pour arrêter, exécutez - notify-send hellovous. si la deuxième commande est exécutée, comment se fait-il que le premier processus soit arrêté? après cela, si le type que fgje ne vois rien se passe, ce qui est évident, car la deuxième commande est déjà exécutée
Edward Torvalds
@edwardtorvalds car les deux commandes sont distinctes. Dans un script, ils devraient être en sous-couche. Écrivez-les dans un script simple et exécutez le script. Ensuite, ctrl-z pour arrêter et vous verrez que la deuxième commande n'est pas exécutée tant que la première n'est pas terminée. Écrire, cmd; cmd; cmd;c'est comme écrire cmd <newline> cmd <newline> .... Alternativement à un script que vous pouvez écrire ( cmd; cmd; cmd; ), il se comporterait comme le script, bacuse du sous-shell généré par(
chaos
j'ai aussi essayé sleep 10. lorsque j'appuie sur CTRL + Z après 3 secondes et que je reprends après quelques secondes et que j'ai remarqué que la commande de sommeil est morte en moins de 7 secondes. ce qui est contraire à ce que vous avez dit, puisque la commande ne s'arrête jamais, elle s'exécute en arrière-plan.
Edward Torvalds
@edwardtorvalds J'ai remarqué ça aussi ... Ça n'a pas de sens. J'ai fait stracela sleepcommande et j'ai découvert que l'appel système utilisé était nanosleep(). Cela semble être un comportement défini de l' nanosleepappel système. restart_syscall()redémarre l'appel système interrompu avec un argument de temps qui est convenablement ajusté pour tenir compte du temps qui s'est déjà écoulé (y compris le temps où le processus a été arrêté par un signal). Lisez cette page de manuel
chaos
Plusieurs notes pour compléter la réponse @chaos (n'hésitez pas à les inclure): quand on fait ctrl-Z, le travail est suspendu ("arrêté"), donc il ne fonctionne pas pour le moment, en attendant que vous décidiez de le continuer au premier plan (ex:) fg %1ou à l'arrière-plan (ex:) bg %1. (si les travaux ne donnent qu'un seul numéro, c'est-à-dire un seul processus suspendu, comme dans l'exemple chaos l'a montré: seulement [1]+ stopped sleep 10, vous pouvez omettre la %npartie. s'il y a plusieurs processus d'arrière-plan (en cours ou arrêtés), vous devez désigner celui que vous voulez avec: %n(ex: fg %2 pour faire reprendre% 2 au premier plan))
Olivier Dulac
6

Si vous voulez simplement suspendre le script tout en restant à l'intérieur du script, vous pouvez utiliser la lecture au lieu de la veille.

Vous pouvez utiliser

read -tpour définir un délai d'attente pour la lecture
read -npour lire un caractère (appuyez simplement sur n'importe quelle touche) pour continuer le script

Comme vous n'avez fourni aucun code, voici un exemple de la façon dont il pourrait être utilisé.
Si vous appuyez sur q, cela read -n1empêche le script de continuer jusqu'à ce qu'une touche soit enfoncée.
Lorsqu'une touche est enfoncée, la vérification est réinitialisée et le script continue normalement dans la boucle.

while [[ true ]]; do
    read -t2 -n1 check
    if [[ $check == "q" ]];then
        echo "pressed"
        read -n1
        check=""
    else
        echo "not pressed"
    fi
echo "Doing Something"
done

Vous pouvez également ajouter stty -echoau début de la section et stty echoà la fin pour empêcher la saisie de gâcher la sortie d'écran


la source
@mikeserv Je ne pense pas qu'op se soucie de ce qui est lu, ils veulent juste mettre en pause et reprendre le script, qu'entendez-vous par enregistrer également les paramètres du terminal, je n'en change qu'un, ce qui semble un peu exagéré.
1
@mikeserv ahh à droite, je présumais simplement que tout stdin proviendrait de l'utilisateur.
1

Avec, ddvous pouvez lire de manière fiable un seul octet à partir d'un fichier. Avec, sttyvous pouvez définir un mincertain nombre d'octets pour qualifier une lecture de terminal et une timesortie en dixièmes de seconde. Combinez ces deux et vous pouvez vous en passer sleepentièrement, je pense, et laissez simplement le délai de lecture du terminal faire le travail pour vous:

s=$(stty -g </dev/tty)
(while stty raw -echo isig time 20 min 0;test -z "$(
dd bs=1 count=1 2>/dev/null; stty "$s")" || (exec sh)
do echo "$SECONDS:" do your stuff here maybe                             
   echo  no sleep necessary, I think                                                          
   [ "$((i+=1))" -gt 10 ] && exit                                                             
done       
) </dev/tty

C'est une petite whileboucle d' exemple que j'ai simulée pour que vous l'essayiez. Toutes les deux secondes dd, sa tentative de lecture stdin- redirigé depuis /dev/tty- et la whileboucle bouclent. Cela ou dd n'expire pas car vous appuyez sur une touche - auquel cas un shell interactif est appelé.

Voici un test - les nombres imprimés en tête de chaque ligne sont la valeur de la variable shell $SECONDS:

273315: do your stuff here maybe
no sleep necessary, I think
273317: do your stuff here maybe
no sleep necessary, I think
273319: do your stuff here maybe
no sleep necessary, I think
273321: do your stuff here maybe
no sleep necessary, I think
sh-4.3$ : if you press a key you get an interactive shell
sh-4.3$ : this example loop quits after ten iterations
sh-4.3$ : or if this shell exits with a non-zero exit status
sh-4.3$ : and speaking of which, to do so you just...
sh-4.3$ exit
exit
273385: do your stuff here maybe
no sleep necessary, I think
273387: do your stuff here maybe
no sleep necessary, I think
273389: do your stuff here maybe
no sleep necessary, I think
273391: do your stuff here maybe
no sleep necessary, I think
273393: do your stuff here maybe
no sleep necessary, I think
273395: do your stuff here maybe
no sleep necessary, I think
273397: do your stuff here maybe
no sleep necessary, I think
mikeserv
la source
Ne devriez-vous pas utiliser stty saneaprès avoir modifié le paramètre stty, je me trompe peut-être, mais il ne semble pas que vous les ayez réinitialisés quelque part?
@Jidder - non, je sauvegarde l'état du terminal en haut du script avec s=$(stty -g </dev/tty). Immédiatement après avoir appelé, ddje le restaure avec stty "$s". L'état du terminal ne se soucie pas des sous-boîtiers et ces paramètres restent donc indépendants du shell parent. stty sanen'est pas nécessairement ce que vous voulez faire - il vaut mieux restaurer l'état tel que vous l'avez trouvé que de supposer que l'état était saneà ce moment-là. Si je ne le restaurais pas, ces echos seraient partout. Comprendre cela est en partie pourquoi je suis arrivé si tard - votre réponse n'était pas là quand j'ai commencé les tests.
mikeserv