Tips en Trucs 2020

Timeout

Met timeout kan je opdrachten na een bepaald aantal seconden afsluiten. Timeout maakt deel uit van de GNU Core Utilities en is dus bij bijna alle distributies standaard geïnstalleerd.

Basisgebruik

Om een opdracht een bepaalde tijd uit te voeren start je timeout met een aantal seconden en een uit te voeren opdracht. Om bijvoorbeeld de opdracht top tien seconden uit te voeren, start je de volgende timeout opdracht:

dany@pindabook:~> timeout 10 top

Na tien seconden wordt de top opdracht automatisch afgesloten.

Andere signalen naar een opdracht sturen

Je kunt verschillende signalen naar een opdracht sturen. Standaard stuurt timeout het signaal SIGTERM. Om zelf te bepalen welk signaal timeout moet sturen, gebruik je de -s (--signal) optie. Zo stuur je na tien seconden het signaal SIGINT naar top met de opdracht:

dany@pindabook:~> timeout -s SIGINT 10 top

Je kunt de signaalnaam ook vervangen door zijn getalwaarde, maar dit is minder leesbaar:

dany@pindabook:~> timeout -s 2 10 top

Een lijst met bruikbare signalen kan je opvragen met:

dany@pindabook:~> kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

Voor meer informatie raadpleeg je WikiPedia en/of de Manual Page.

Vertel wat je doet

Om te weten welk signaal timeout naar een opdracht stuurt, gebruik je de -v (--verbose) optie. Dit toont het verstuurde signaal als foutmelding (stderr, standard error). Om bijvoorbeeld vijf seconden de logboeken van het printsysteem CUPS te volgen, gebruik je

dany@pindabook:~> timeout -v 5 tail -F /var/log/cups/access_log 
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:17:01 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:13:19:35 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:14:17:55 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
localhost - - [24/Jul/2020:15:16:15 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
timeout: sending signal TERM to command ‘tail’

Daarbij heeft timeout het signaal SIGTERM gebruikt om de tail opdracht te beëindigen. Een ander voorbeeld als bevestiging:

dany@pindabook:~> timeout -v -s SIGINT 5 tail -F /var/log/cups/access_log 
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:17:01 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:13:19:35 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:14:17:55 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
localhost - - [24/Jul/2020:15:16:15 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
timeout: sending signal INT to command ‘tail’

Afsluitcode (Exit Code)

Timeout sluit af met de code 124 als de opdracht na het verstrijken van de tijdslimiet door timeout werd afgesloten. Indien de opdracht werd afgesloten voor het verlopen van de tijdslimiet, wordt de afsluitcode van de opdracht doorgegeven. Wil je echter altijd de afsluitcode van de door timeout gestarte opdracht behouden, gebruik je de optie --preserve-status option.

Opnieuw gebruiken we de tail opdracht als voorbeeld, maar deze keer sluiten we deze af met de toetscombinatie Ctrl+c (SIGINT). Daardoor krijg je afsluitcode 130 (afsluiten met Ctrl+c).

dany@pindabook:~> tail -F /var/log/cups/access_log 
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:17:01 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:13:19:35 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:14:17:55 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
localhost - - [24/Jul/2020:15:16:15 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
^C
dany@pindabook:~> echo $?
130

Nu laten we timeout na 5 seconden de tail opdracht automatisch afsluiten:

dany@pindabook:~> timeout -s SIGINT 5 tail -F /var/log/cups/access_log 
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:17:01 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:13:19:35 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:14:17:55 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
localhost - - [24/Jul/2020:15:16:15 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
dany@pindabook:~> echo $?
124

De afsluitcode 124 is afkomstig van timeout, en niet van de tail opdracht. Als we na het verlopen van de tijdslimiet toch de afsluitcode van de gestarte opdracht willen, gebruik je de optie --preserve-status:

dany@pindabook:~> timeout --preserve-status -s SIGINT 5 tail -F /var/log/cups/access_log 
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [20/Jul/2020:15:23:07 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:17:01 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 414 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:10:43:24 +0200] "POST / HTTP/1.1" 200 152 Cancel-Subscription successful-ok
localhost - - [24/Jul/2020:13:19:35 +0200] "POST / HTTP/1.1" 200 562 Create-Printer-Subscriptions successful-ok
localhost - - [24/Jul/2020:14:17:55 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
localhost - - [24/Jul/2020:15:16:15 +0200] "POST / HTTP/1.1" 200 183 Renew-Subscription successful-ok
dany@pindabook:~> echo $?
130

Losgeslagen opdrachten hardhandig afsluiten

Soms reageert een opdracht niet op een SIGTERM signaal en blijft maar verder werken. Je krijgt dezelfde situatie als je een opdracht niet met Ctrl+c kunt afsluiten. Dan wordt er al vlug naar de noodrem kill -9 (SIGKILL) gegrepen. Ook timeout heeft daarvoor een oplossing.

De -k (--kill-after) optie stuurt een SIGKILL signaal na een opgegeven tijdslimiet. Om dit te demonstreren, schrijven we een script dat het SIGTERM signaal negeert:

#!/bin/bash
trap 'echo "SIGTERM ontvangen, reset teller en verder negeren."; SECS=1' TERM 
SECS=1
while true; do
  echo "Opdracht werkt $SECS seconden…"
  let "SECS=SECS+1"
  sleep 1
done

We maken het script uitvoerbaar met:

dany@pindabook:~> chmod +x nosigterm.sh

Daarna voeren we het script uit via timeout:

dany@pindabook:~> timeout -k 10 5 ./nosigterm.sh 
Opdracht werkt 1 seconden…
Opdracht werkt 2 seconden…
Opdracht werkt 3 seconden…
Opdracht werkt 4 seconden…
Opdracht werkt 5 seconden…
Beëindigd
SIGTERM ontvangen, reset teller en verder negeren.
Opdracht werkt 1 seconden…
Opdracht werkt 2 seconden…
Opdracht werkt 3 seconden…
Opdracht werkt 4 seconden…
Opdracht werkt 5 seconden…
Opdracht werkt 6 seconden…
Opdracht werkt 7 seconden…
Opdracht werkt 8 seconden…
Opdracht werkt 9 seconden…
Opdracht werkt 10 seconden…
Geëlimineerd

Je merkt duidelijk dat timeout probeert om na vijf seconden het script af te sluiten, maar het script negeert dit. Na deze poging blijft het script nog tien seconden verder werken tot het gedwongen wordt afgesloten met een SIGKILL signaal van timeout.
Timeout