This blog is part of a bash tips list I find useful to use on every script -- the whole list can be found here.
No doubt, all these guys who created Unix/Linux OS and commands are brilliant. We already saw that something like pausing and restarting a process which look complicated is very easy, now let's have a look at how to run a command for a certain amount of time (how to stop a command after a certain amount of time).
Let's start using sleep as example:
Note that timeout works with simple command and you have to use a different syntax if you want to use it with more complex command like for loops, while loops, stuff like that. If we use the for loop I have used in pause/restart a process with timeout, this won't work as is:
That's all for this one, enjoy this new jewel !
No doubt, all these guys who created Unix/Linux OS and commands are brilliant. We already saw that something like pausing and restarting a process which look complicated is very easy, now let's have a look at how to run a command for a certain amount of time (how to stop a command after a certain amount of time).
Let's start using sleep as example:
$ date; sleep 20; date Sun Sep 19 22:27:35 AEST 2021 Sun Sep 19 22:27:55 AEST 2021 <== sleep 20 stopped after 20 seconds as epxected $Now let's say we want to stop this sleep 20 comand after 10 seconds; how would you do that ? it does not seem that easy, right ? unless you know the . . . timeout command ! have a look below:
$ date; timeout 10 sleep 20; date Sun Sep 19 22:36:42 AEST 2021 Sun Sep 19 22:36:52 AEST 2021 <== sleep 20 stopped after 10 seconds ! $It was easy right ? now let's take a real life example. Let's say you want to run a tcpdump on a port and filter on few IPs, tcpdump generating a very large amount of data (some systems can generate 500+ MB for 1 second), you do not want to run tcpdump for too long. tcpdump is cool enough to provide a -G option to rotate to a new file every -G seconds (-G 1 will rotate the files every 1 second) if the output file specified by the -w option has a timeformat -- below an example to show this:
# tcpdump -G 1 'port 1521 and (host 10.11.12.13 or host 10.11.12.14 or host 10.11.12.15)' -w $(hostname)-%Y-%m-%d_%H.%M.%SExplanation:
- tcpdump -G 1: take a tcpdump and rotate the output file every 1 second
- port 1521 and (host 10.11.12.13 or host 10.11.12.14 or host 10.11.12.15): tcpdump port 1521 and filter on these 3 IPs
- -w $(hostname)-%Y-%m-%d_%H.%M.%S: save the output in a file with the hostname and a timestamp (which will be changed every second)
# date; timeout 20 tcpdump -G 1 'port 1521 and (host 10.11.12.13 or host 10.11.12.14 or host 10.11.12.15)' -w $(hostname)-%Y-%m-%d_%H.%M.%S; date Sun Sep 19 14:51:35 AEST 2021 tcpdump: listening on bondeth0, link-type EN10MB (Ethernet), capture size 262144 bytes xxxxxx packets captured yyyyyy packets received by filter zzzzzz packets dropped by kernel Sun Sep 19 14:51:55 AEST 2021 <== stopped after 20 seconds #Here you go, a 20 seconds tcpdump which has generated 20 files containing 1 second each. You want to schedule it in like 2 hours ? open a screen and:
# date; sleep 7200; date; timeout 20 tcpdump -G 1 'port 1521 and (host 10.11.12.13 or host 10.11.12.14 or host 10.11.12.15)' -w $(hostname)-%Y-%m-%d_%H.%M.%S; dateEasy peasy !
Note that timeout works with simple command and you have to use a different syntax if you want to use it with more complex command like for loops, while loops, stuff like that. If we use the for loop I have used in pause/restart a process with timeout, this won't work as is:
$ timeout 6 "for i in {1..100}; do echo \$(date) -- \$i; sleep 2; done" timeout: failed to run command ‘for i in {1..100}; do echo $(date) -- $i; sleep 2; done’: No such file or directory $You have to use the below syntax starting the command in a bash subshell:
$ timeout 6 bash -c "for i in {1..100}; do echo \$(date) -- \$i; sleep 2; done" Sun Sep 19 23:46:05 AEST 2021 -- 1 Sun Sep 19 23:46:07 AEST 2021 -- 2 Sun Sep 19 23:46:09 AEST 2021 -- 3 <== stopped after 6 seconds as per timeout $Or with a script:
$ cat myscript.sh #!/bin/bash for i in {1..100}; do echo $(date) -- $i; sleep 2; done $ timeout 6 ./myscript.sh Sun Sep 19 23:54:27 AEST 2021 -- 1 Sun Sep 19 23:54:29 AEST 2021 -- 2 Sun Sep 19 23:54:31 AEST 2021 -- 3 <== stopped after 6 seconds as per timeout $
That's all for this one, enjoy this new jewel !
Really cool sir. Thank you very much for this post.
ReplyDeleteThis is a great command that I have used quite a bit with our patching. I even appreciate the additional options to send a specific signal and the ability to send a kill signal if the command doesn't respond accordingly after the initial timeout.
ReplyDeleteIndeed -- maybe I should add a paragraph about this as well.
Delete