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.
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.
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.
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’
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
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.