Jak mogę obudzić skrypt śpiącego basha?

27

Czy można obudzić proces wstrzymany za pomocą sleeppolecenia?


Na przykład załóżmy, że masz ten skrypt:

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

Po 30 minutach odkrywasz, że potrzebujesz zatrzymać skrypt, to znaczy żałowałeś, że nie napisałeś sleep 30m.

Nie chcesz dzwonić kill PIDani naciskać Ctrl+ C, ponieważ wtedy ostatnie polecenie nie zostanie wykonane i będziesz zmęczony.

Czy istnieje sposób na sleepwybudzenie procesu lub użycie innego polecenia obsługującego wybudzanie? Mile widziane są rozwiązania zarówno procesów w tle, jak i na pierwszym planie.

Bittenus
źródło
13
Krzycz na to naprawdę głośno.
Klamka
2
@Doorknob gawking naprawdę nie działa. Ostatnim razem, gdy miałem sleepproces ing, zrzuciłem pushdgo z łóżka.
imallett
W twoim skrypcie brakuje #!wiersza. I to ma znaczenie, ponieważ odpowiedź na twoje pytanie zależy od tego, czy coś jest -ew #!linii.
kasperd
1
@kasperd Gotowe. Z ciekawości: jaki wpływ ma flaga -e?
Bittenus
2
Domyślnie skrypt będzie kontynuował działanie po błędzie. Jeśli użyjesz #!/bin/bash -e, skrypt zatrzyma się po błędzie. Po prostu zabicie komendy uśpienia będzie traktowane jako błąd przez bash. Oznacza to, -eże bez odpowiedzi na twoje pytanie jest dość prosta. Jeśli -ezostanie użyty, stanie się o wiele trudniejszy, ponieważ musisz zatrzymać proces snu bez zabijania go.
kasperd

Odpowiedzi:

47

Kiedy skrypt Bash uruchamia a sleep, oto jak pstreemoże wyglądać:

bash(10102)───sleep(8506)

Oba mają identyfikatory procesów (PID), nawet jeśli działają jako skrypty. Gdybyśmy chcieli przerwać sen, wysłalibyśmy kill 8506i sesja Bash wznowiłaby ... Problem polega na tym, że w środowisku skryptowym nie znamy PID sleeppolecenia i nie ma człowieka, aby spojrzeć na proces drzewo.

Możemy uzyskać PID sesji Bash poprzez $$zmienną magiczną. Jeśli możemy to gdzieś przechowywać, możemy następnie celować w instancje sleep, które działają pod tym PID. Oto, co umieściłem w skrypcie:

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

# go to sleep for a long time
sleep 1000

A potem możemy powiedzieć instancjom pkillnuklearnym sleepdziałającym pod tym PID:

pkill -P $(<myscript.pid) sleep

Ponownie ogranicza się to tylko do sleepprocesów działających bezpośrednio w ramach jednej sesji Bash. Tak długo, jak PID był poprawnie zalogowany, czyni go znacznie bezpieczniejszym niż killall sleeplub pkill sleep, co może spowodować uszkodzenie dowolnego sleep procesu w systemie (zezwalając na uprawnienia).

Możemy udowodnić tę teorię na następującym przykładzie, w którym mamy trzy osobne sesje bash, dwie uruchomione sleep. Tylko dlatego, że określamy PID w lewej górnej sesji bash, tylko jej sleepjest zabijana.

wprowadź opis zdjęcia tutaj


Alternatywnym podejściem jest przesunięcie sleepw tło, zapisanie jego PID, a następnie powrót na pierwszy plan. W skrypcie:

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

I zabić to:

kill $(<myscript.sleep.pid)
Oli
źródło
5

Możesz napisać skrypt do obsługi („pułapki”) innych sygnałów z zabicia itp., Aby w razie potrzeby zmodyfikować zachowanie skryptu. Zobacz 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
źródło
4

Możesz po prostu zabić sen, który przejdzie do następnego wiersza skryptu:

pkill sleep

Zauważ, że zabiłoby to każdy proces uśpienia uruchomiony w twoim systemie, nie tylko w twoim skrypcie.

Animaletdesequia
źródło
1

Mam skrypt do spania uruchamiany cronprzy uruchamianiu. Skrypt budzi się co minutę i ustawia jasność wyświetlacza laptopa na podstawie wschodu i zachodu słońca uzyskanego z Internetu. Konfigurowalna przez użytkownika faza przejścia między pełnym rozjaśnieniem a pełnym przyciemnieniem wymaga zwiększania i zmniejszania wartości o 3, 4, 5 lub cokolwiek obliczanego co minutę.

Oli krótko poruszył pstreeswoją odpowiedź, ale odrzucił ją, ponieważ zabiłoby to wszystkie sleepprzypadki. Można tego uniknąć, zawężając wyszukiwanie za pomocą opcji pstree.

Używając pstree -hwidzimy całą heirarchię:

$ 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

Jak widać typowy login Ubuntu zawiera wiele wielu identyfikatorów PID (ID procesów).

Możemy zawęzić go do naszego działającego skryptu, używając:

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

Widzimy:

  • cron uruchomił powłokę (identyfikator procesu 1308 i identyfikator sesji 1308)
  • Powłoka wywołuje nasz program działający pod identyfikatorem procesu 1321 i identyfikatorem sesji 1308 (pasującym do powłoki)
  • Nasz program wywołuje sleepproces o numerze ID 26552 i ponownie o sesji o numerze 1308

W tym momencie możemy użyć pkill -s 1308i zabiłoby to całą sesję, która zawiera powłokę, nasz program display-auto-brightnessi sleeppolecenie. Zamiast tego użyjemy kill 26552tylko do zabicia polecenia uśpienia, co zmusi nasz program do przebudzenia i dostosowania jasności.

Wpisując to ręcznie w terminalu zobaczysz:

───────────────────────────────────────────────────────────────────────────────
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:~$ 

Następnym krokiem jest zrobienie tego, gdy laptop budzi się z zawieszenia. Na przykład po zamknięciu pokrywy było całkowicie ciemno, a jasność ekranu ustawiono na „300”. Po otwarciu pokrywy jest światło dzienne, a jasność należy ustawić na „2000”. Oczywiście program sam się obudzi w ciągu 1 do 59 sekund, ale wygodniej jest natychmiast ustawić jasność.

Po napisaniu opublikuję kod zawieszenia / wznowienia. Mam nadzieję, że w ten weekend.

WinEunuuchs2Unix
źródło