Twitter

Some bash tips -- 10 -- Run a command for a certain amount of time

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:
$ 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.%S
Explanation:
  • 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)
Now that we have understood this, we want to run this tcpdump command for few seconds, let's say maximum a few minutes. So you can manually start it and CTRL-C it when you are done but first of all it is not classy (:D) and what if you need to schedule it ? this is where timeout comes into the game !
# 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; date
Easy 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 !


< Previous bash tip / Next bash tip >

3 comments:

  1. Really cool sir. Thank you very much for this post.

    ReplyDelete
  2. This 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.

    ReplyDelete
    Replies
    1. Indeed -- maybe I should add a paragraph about this as well.

      Delete

Some bash tips -- 18 -- paste

This blog is part of a shell tips list which are good to know -- the whole list can be found here. I really like finding a real usage for...