Comment puis-je réveiller un script bash endormi?

27

Est-il possible de réveiller un processus interrompu à l'aide de la sleepcommande?


À titre d'exemple, disons que vous avez ce script:

#!/bin/bash
echo "I am tired"
sleep 8h
echo "I am fresh :)"

Après 30 minutes, vous découvrez que vous avez besoin que le script s'arrête, c'est-à-dire que vous souhaitiez avoir écrit à la sleep 30mplace.

Vous ne voulez ni appeler kill PIDni appuyer sur Ctrl+ C, car la dernière commande n'est pas exécutée et vous resterez fatigué.

Existe-t-il un moyen de réveiller le processus sleepou peut-être d'utiliser une autre commande qui prend en charge le réveil? Les solutions aux processus d'arrière-plan et de premier plan sont les bienvenues.

Bittenus
la source
13
Criez dessus très fort.
Poignée de porte
2
@ Doorknob gawking ne fonctionne pas vraiment. La dernière fois que j'ai eu un sleepprocessus d'ing, je l'ai pushdsorti du lit.
imallett
Il manque une #!ligne à votre script . Et que les choses parce que la réponse à votre question dépend de savoir s'il y a une -edans la #!ligne.
kasperd
1
@kasperd Done. Par curiosité: quelle influence le drapeau -e a-t-il?
Bittenus
2
Par défaut, un script continuera après une erreur. Si vous utilisez #!/bin/bash -e, le script s'arrêtera après une erreur. Le simple fait de tuer la commande sleep sera traité comme une erreur par bash. Cela signifie que sans -eil existe une réponse assez simple à votre question. S'il a -eété utilisé, cela devient beaucoup plus difficile, car vous devrez arrêter le processus de sommeil sans le tuer.
kasperd

Réponses:

47

Lorsqu'un script Bash exécute un sleep, voici à quoi cela pstreepourrait ressembler:

bash(10102)───sleep(8506)

Les deux ont des ID de processus (PID), même lorsqu'ils s'exécutent en tant que script. Si nous voulions interrompre le sommeil, nous enverrions kill 8506et la session Bash reprendrait ... Le problème est dans un environnement scripté, nous ne connaissons pas le PID de la sleepcommande et il n'y a pas d'humain pour regarder le processus arbre.

Nous pouvons obtenir le PID de la session Bash via la $$variable magique. Si nous pouvons stocker cela quelque part, nous pouvons alors cibler des instances sleepqui s'exécutent sous ce PID. Voici ce que je mettrais dans le script:

# write the current session's PID to file
echo $$ >> myscript.pid

# go to sleep for a long time
sleep 1000

Et puis nous pouvons dire pkillaux sleepinstances de Nuke qui s'exécutent sous ce PID:

pkill -P $(<myscript.pid) sleep

Encore une fois, cela se limite aux seuls sleepprocessus s'exécutant directement sous cette seule session Bash. Tant que le PID a été enregistré correctement, cela le rend beaucoup plus sûr que killall sleepou pkill sleep, ce qui pourrait neutraliser tout sleep processus sur le système (autorisations le permettant).

Nous pouvons prouver cette théorie avec l'exemple suivant où nous avons trois sessions bash distinctes, deux en cours d'exécution sleep. Seulement parce que nous spécifions le PID de la session bash en haut à gauche, seul son sleepest tué.

entrez la description de l'image ici


Une autre approche consiste à pousser sleepen arrière-plan, à stocker son PID, puis à le remettre au premier plan. Dans le script:

sleep 1000 &
echo $! > myscript.sleep.pid
fg

Et pour le tuer:

kill $(<myscript.sleep.pid)
Oli
la source
5

Vous pouvez écrire votre script pour gérer ("intercepter") d'autres signaux provenant de kill, etc. afin de pouvoir modifier le comportement des scripts selon vos besoins. Voir man bash:

SIGNALS
   When  bash  is  interactive,  in the absence of any traps, it ignores SIGTERM (so that kill 0 does not
   kill an interactive shell), and SIGINT is caught and handled (so that the wait builtin  is  interrupt-
   ible).   In all cases, bash ignores SIGQUIT.  If job control is in effect, bash ignores SIGTTIN, SIGT-
   TOU, and SIGTSTP.

   Non-builtin commands run by bash have signal handlers set to the values inherited by  the  shell  from
   its  parent.   When  job  control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in
   addition to these inherited handlers.  Commands run as a result of  command  substitution  ignore  the
   keyboard-generated job control signals SIGTTIN, SIGTTOU, and SIGTSTP.

   The shell exits by default upon receipt of a SIGHUP.  Before exiting, an interactive shell resends the
   SIGHUP to all jobs, running or stopped.  Stopped jobs are sent SIGCONT to ensure that they receive the
   SIGHUP.   To  prevent the shell from sending the signal to a particular job, it should be removed from
   the jobs table with the disown builtin (see SHELL BUILTIN COMMANDS below) or  marked  to  not  receive
   SIGHUP using disown -h.

   If  the huponexit shell option has been set with shopt, bash sends a SIGHUP to all jobs when an inter-
   active login shell exits.

   If bash is waiting for a command to complete and receives a signal for which a trap has been set,  the
   trap  will not be executed until the command completes.  When bash is waiting for an asynchronous com-
   mand via the wait builtin, the reception of a signal for which a trap has been set will cause the wait
   builtin  to  return immediately with an exit status greater than 128, immediately after which the trap
   is executed.
afbach
la source
4

Vous pouvez simplement tuer le sommeil qui continuerait jusqu'à la ligne suivante du script:

pkill sleep

Notez que cela tuerait tout processus de veille en cours d'exécution dans votre système, pas seulement dans votre script.

animaletdesequia
la source
1

J'ai un script bash endormi démarré cronau démarrage. Le script se réveille chaque minute et définit la luminosité de l'écran du portable en fonction du lever et du coucher du soleil obtenus sur Internet. Une phase de transition configurable par l'utilisateur entre la pleine luminosité et la pleine luminosité nécessite une augmentation et une diminution des valeurs de 3, 4, 5 ou tout ce qui est calculé toutes les minutes.

Oli a brièvement abordé pstreesa réponse mais l'a rejetée car cela tuerait tous les sleepcas. Cela peut être évité en restreignant la recherche à l'aide des options pstree.

En utilisant, pstree -hnous voyons toute la hiérarchie:

$ pstree -h
systemd─┬─ModemManager─┬─{gdbus}
                      └─{gmain}
        ├─NetworkManager─┬─dhclient
                        ├─dnsmasq
                        ├─{gdbus}
                        └─{gmain}
        ├─accounts-daemon─┬─{gdbus}
                         └─{gmain}
        ├─acpid
        ├─agetty
        ├─atd
        ├─avahi-daemon───avahi-daemon
        ├─cgmanager
        ├─colord─┬─{gdbus}
                └─{gmain}
        ├─cron───cron───sh───display-auto-br───sleep
        ├─cups-browsed─┬─{gdbus}
                      └─{gmain}
        ├─dbus-daemon
        ├─fwupd─┬─3*[{GUsbEventThread}]
               ├─{fwupd}
               ├─{gdbus}
               └─{gmain}
        ├─gnome-keyring-d─┬─{gdbus}
                         ├─{gmain}
                         └─{timer}
        ├─irqbalance
        ├─lightdm─┬─Xorg───3*[{Xorg}]
                 ├─lightdm─┬─upstart─┬─at-spi-bus-laun─┬─dbus-daemon
                                                    ├─{dconf worker}
                                                    ├─{gdbus}
                                                    └─{gmain}
                                   ├─at-spi2-registr─┬─{gdbus}
                                                    └─{gmain}
                                   ├─bamfdaemon─┬─{dconf worker}
                                               ├─{gdbus}
                                               └─{gmain}
                                   ├─chrome─┬─2*[cat]
                                           ├─chrome─┬─chrome─┬─2*[chrome─┬─{Chrome_ChildIOT}]
                                                                      ├─5*[{CompositorTileW}]]
                                                                      ├─{Compositor}]
                                                                      ├─{GpuMemoryThread}]
                                                                      ├─{MemoryInfra}]
                                                                      ├─{Renderer::FILE}]
                                                                      ├─{TaskSchedulerRe}]
                                                                      └─{TaskSchedulerSe}]
                                                           ├─7*[chrome─┬─{Chrome_ChildIOT}]
                                                                      ├─5*[{CompositorTileW}]]
                                                                      ├─{Compositor}]
                                                                      ├─{GpuMemoryThread}]
                                                                      ├─{MemoryInfra}]
                                                                      ├─{Renderer::FILE}]
                                                                      ├─{ScriptStreamerT}]
                                                                      ├─{TaskSchedulerRe}]
                                                                      └─{TaskSchedulerSe}]
                                                           ├─chrome─┬─{Chrome_ChildIOT}
                                                                   ├─5*[{CompositorTileW}]
                                                                   ├─{Compositor}
                                                                   ├─{GpuMemoryThread}
                                                                   ├─{Media}
                                                                   ├─{MemoryInfra}
                                                                   ├─{Renderer::FILE}
                                                                   ├─{ScriptStreamerT}
                                                                   ├─{TaskSchedulerRe}
                                                                   └─{TaskSchedulerSe}
                                                           └─2*[chrome─┬─{Chrome_ChildIOT}]
                                                                       ├─5*[{CompositorTileW}]]
                                                                       ├─{Compositor}]
                                                                       ├─{GpuMemoryThread}]
                                                                       ├─{Renderer::FILE}]
                                                                       ├─{ScriptStreamerT}]
                                                                       ├─{TaskSchedulerRe}]
                                                                       └─{TaskSchedulerSe}]
                                                   └─nacl_helper
                                           ├─chrome─┬─chrome
                                                   ├─{Chrome_ChildIOT}
                                                   ├─{MemoryInfra}
                                                   ├─{TaskSchedulerSe}
                                                   └─{Watchdog}
                                           ├─{AudioThread}
                                           ├─{BrowserWatchdog}
                                           ├─{Chrome_CacheThr}
                                           ├─{Chrome_DBThread}
                                           ├─{Chrome_FileThre}
                                           ├─{Chrome_FileUser}
                                           ├─{Chrome_HistoryT}
                                           ├─{Chrome_IOThread}
                                           ├─{Chrome_ProcessL}
                                           ├─{Chrome_SyncThre}
                                           ├─{CompositorTileW}
                                           ├─{CrShutdownDetec}
                                           ├─{D-Bus thread}
                                           ├─{Geolocation}
                                           ├─{IndexedDB}
                                           ├─{LevelDBEnv}
                                           ├─{MemoryInfra}
                                           ├─{NetworkChangeNo}
                                           ├─{Networking Priv}
                                           ├─4*[{TaskSchedulerBa}]
                                           ├─6*[{TaskSchedulerFo}]
                                           ├─{TaskSchedulerSe}
                                           ├─{WorkerPool/3166}
                                           ├─{WorkerPool/5824}
                                           ├─{WorkerPool/5898}
                                           ├─{WorkerPool/6601}
                                           ├─{WorkerPool/6603}
                                           ├─{WorkerPool/7313}
                                           ├─{chrome}
                                           ├─{dconf worker}
                                           ├─{extension_crash}
                                           ├─{gdbus}
                                           ├─{gmain}
                                           ├─{gpu-process_cra}
                                           ├─{inotify_reader}
                                           ├─{renderer_crash_}
                                           ├─{sandbox_ipc_thr}
                                           └─{threaded-ml}
                                   ├─compiz─┬─{dconf worker}
                                           ├─{gdbus}
                                           ├─{gmain}
                                           └─8*[{pool}]
                                   ├─conky───6*[{conky}]
                                   ├─2*[dbus-daemon]

( .... many lines deleted to fit in 30k limit .... )

        ├─vnstatd
        ├─whoopsie─┬─{gdbus}
                  └─{gmain}
        └─wpa_supplicant

Comme vous pouvez le voir, une connexion Ubuntu typique contient de nombreux PID (ID de processus).

Nous pouvons le réduire à notre script en cours d'exécution en utilisant:

$ pstree -g -p | grep display-auto
  |-cron(1198,1198)---cron(1257,1198)---sh(1308,1308)---display-auto-br(1321,1308)---sleep(26552,1308)

Nous voyons:

  • cron a démarré un shell (ID de processus 1308 et ID de session 1308)
  • Le shell appelle notre programme exécuté sous l'ID de processus 1321 et l'ID de session 1308 (correspondant au shell)
  • Notre programme appelle sleepsous l'ID de processus 26552 et à nouveau l'ID de session 1308

À ce stade, nous pouvons utiliser pkill -s 1308et cela tuerait la session entière qui comprend le shell, notre programme display-auto-brightnesset la sleepcommande. Au lieu de cela, nous utiliserons kill 26552uniquement la commande sleep pour forcer notre programme à se réveiller et à régler la luminosité.

En tapant cela manuellement dans le terminal, vous voyez:

───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ pstree -g -p | grep display-auto
             |-cron(1198,1198)---cron(1257,1198)---sh(1308,1308)---display-auto-br(1321,1308)---sleep(32362,1308)
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ sudo kill 32362
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ pstree -g -p | grep display-auto
             |-cron(1198,1198)---cron(1257,1198)---sh(1308,1308)---display-auto-br(1321,1308)---sleep(1279,1308)
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ sudo kill 1279
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ pstree -g -p | grep display-auto
             |-cron(1198,1198)---cron(1257,1198)---sh(1308,1308)---display-auto-br(1321,1308)---sleep(4440,1308)
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ 

L'étape suivante consiste à le faire lorsque l'ordinateur portable se réveille de la suspension. Par exemple, lorsque le couvercle était fermé, il faisait noir et la luminosité de l'écran était réglée sur "300". Lorsque le couvercle est ouvert, il fait jour et la luminosité doit être réglée sur "2000". Bien sûr, le programme se réveillerait de lui-même en 1 à 59 secondes, mais il est plus confortable de régler la luminosité instantanément.

Je publierai le code de suspension / reprise après son écriture. Espérons que ce week-end.

WinEunuuchs2Unix
la source