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

CUDA: Getting started on Google Colab

While getting started with CUDA on Windows or on WSL (same on Linux) requires to install some stuff, it is not the case when using Google...