bash scripting

ilovelinux

New Member
Joined
Mar 15, 2018
Messages
7
Reaction score
6
Credits
0
Hi,

I am new to bash scripting. I am seeking for your help on how to create a tcpdump scripting. The objective is to kill the tcpdump process at a certain time. I hope you could help me on this please. Thank you in advance
 


That would be pretty simple.
Your script would need to:
1. Start the tcpdump command in the background
2. Get the PID of the tcpdump process using $! and store it in a variable
3. Sleep for the required amount of time
4. Kill the tcpdump process using the stored PID.

e.g.
Code:
#!/usr/bin/env bash
tcpdump -w ~/Desktop/output.pcap -i wlan0 tcp &
PROCESS_ID=$!
sleep 30
kill -15 $PROCESS_ID

The above script starts tcpdump in the background and gets the PID of tcpdump.
It sleeps for 30 seconds and then kills the tcpdump process.

That would be the simplest way of starting a command and running it for a certain amount of time.

NOTES:
1. Replace my example tcpdump command with your tcpdump command.
I don't have tcpdump installed on my Linux laptop ATM and I haven't used tcpdump for a long time, so my example command might not be valid - it was just off the top of my head.

2. If I recall correctly - on my Debian system, tcpdump has to be started as root. So in order for the tcpdump command to work - the script might have to be ran as root. However, if you are using Kali - in which you are always running as root - then you don't need to worry about that.

3. If you wanted to do something more complicated, like killing the process at a particular time (e.g. 14:00hrs) - the simplest way to do that would be to calculate the number of seconds from now until the required time and make your script sleep for that number of seconds.

4. The above script does not actually check whether the tcpdump process actually started.
So after starting tcpdump and getting the PID of the process - it might also be an idea to check that the process did start.
If the process failed to start - then you could simply exit (rather than sleeping and then trying to kill a process that doesn't exist.

Other improvements:
There are a number of other improvements that could be made to the above script.
You could make it more general-purpose by changing it to take some parameters.
For example:
You could pass the command to run and the amount of time to sleep for (or the actual time to stop the process) as parameters to the script.

That way, the script will be able to run any command and then kill it at a certain time, or after a certain number of minutes.

[EDIT]
Admins: This thread might be worth moving to the command-line section.
I don't think it belongs here in general server
[/EDIT]
 
Last edited:
Hi Jaskinakis,

Thank you for your help. By the way here is my sample script I did:

#!/bin/bash
#get the process ID

pid=$ (ps -ef | grep -x '[p]ing' | awk ' { print $2 } '
#terminate
kill -15 $pid (ps -ef | grep '[p]ing' | awk '{ print $2 } '
ping = $pid (date +'%Y-%m-%)

I need to kill the tcpdump process in certain date and time. I will run it to the cron.
 
OK. I see.

I thought you wanted a script that would start tcpdump and then end it at a specified time.

But what you really want is something to get the PID of an already running tcpdump and kill it at a specified date/time. Is that correct?

There are multiple problems with the script you've posted.
  • Line 4 - there is a space between $ and ( - that space should not be there
  • Line 4 - the -x switch to grep will match the regex against the whole line.
    In other words the whole line output from ps would have to exactly match '[p]ing'. So you'd get no matches from that, because the line from ps would be a lot longer!
  • Line 4 - missing ) at the end of the line
  • Line 6 - all you need here is "kill -15 $pid". The rest of the line is not needed.
  • Line 7 - I don't know what you are trying to do here. Is the = supposed to be a test for equality? Or an assignment? If it's an assignment, there should be no spaces between the two operands either way the whole line is nonsense from a syntactical point of view!
If we are dealing with an already running instance of tcpdump, then you could use a script like this:
Code:
#!/usr/bin/env bash
pid=$(ps -ef | \grep tcpdump | \grep -v grep | awk '{print $2}')
if [[ $pid ]]; then
    kill -15  $pid
fi

And set it up as a cron job to run at the specified date/time. That would definitely be a way to kill an existing process at a specified date and time.

NOTE: Regarding my usage of grep in the above.
The reason I'm using \grep is in case you have any aliases set up for grep.
I often have grep aliased to "grep=grep -n --color" in my .bashrc. which colourizes the output from grep and prints the line number at the beginning of the output for matched lines.

So I use \grep to escape any aliases and just use grep without any fancy formatting options. This ensures that grep doesn't add any additional fields to matching lines in the output from ps.

Also, the reason I've used two grep commands is because in the following ps command (with one \grep):
Code:
ps -ef | \grep tcpdump | awk '{print $2}'
In the output from the ps command - \grep would match the line containing the tcpdump process that we are interested in AND the line containing the process running the "ps -ef | \grep tcpdump | awk '{print $2}'" command - because it also contains "tcpdump".
So awk would then populate $pid with two pids.

The kill command afterwards would sucessfully kill the tcpdump process, but when it tries to kill the process that was running "ps -ef | blah blah blah" - kill will complain about trying to kill a non-existent process, because at that point the process running the "ps...." command would have already ended.

So the 2nd \grep filters out the "ps -ef | blah blah blah" line - preventing it's PID from being added to $pid and preventing an error message from kill.

One final consideration - if you have multiple tcpdump commands running, the above script would end up killing ALL running tcpdump processes.

If that is the case, and you only want to end ONE particular instance of tcpdump - you would need to another call to \grep to narrow in on the exact process you are looking for - based on some unique criteria that will identify it from the others.
e.g.
Perhaps it is the only one that is looking on wlan0 for udp packets? Or maybe the start-time of the process?

Either way - if there are several tcpdumps running - and you want to end a specific one - you will need to change the above script to \grep the output from ps for something that will uniquely identify the process you are after.

Hope this helps!
If you have any questions - fire away!
 
Last edited:
OK. I see.

I thought you wanted a script that would start tcpdump and then end it at a specified time.

But what you really want is something to get the PID of an already running tcpdump and kill it at a specified date/time. Is that correct?

There are multiple problems with the script you've posted.
  • Line 4 - there is a space between $ and ( - that space should not be there
  • Line 4 - the -x switch to grep will match the regex against the whole line.
    In other words the whole line output from ps would have to exactly match '[p]ing'. So you'd get no matches from that, because the line from ps would be a lot longer!
  • Line 4 - missing ) at the end of the line
  • Line 6 - all you need here is "kill -15 $pid". The rest of the line is not needed.
  • Line 7 - I don't know what you are trying to do here. Is the = supposed to be a test for equality? Or an assignment? If it's an assignment, there should be no spaces between the two operands either way the whole line is nonsense from a syntactical point of view!
If we are dealing with an already running instance of tcpdump, then you could use a script like this:
Code:
#!/usr/bin/env bash
pid=$(ps -ef | \grep tcpdump | \grep -v grep | awk '{print $2}'
if [[ $pid ]]; then
    kill -15  $pid
fi

And set it up as a cron job to run at the specified date/time. That would definitely be a way to kill an existing process at a specified date and time.

NOTE: Regarding my usage of grep in the above.
The reason I'm using \grep is in case you have any aliases set up for grep.
I often have grep aliased to "grep=grep -n --color" in my .bashrc. which colourizes the output from grep and prints the line number at the beginning of the output for matched lines.

So I use \grep to escape any aliases and just use grep without any fancy formatting options. This ensures that grep doesn't add any additional fields to matching lines in the output from ps.

Also, the reason I've used two grep commands is because in the following ps command (with one \grep):
Code:
ps -ef | \grep tcpdump | awk '{print $2}'
In the output from the ps command - \grep would match the line containing the tcpdump process that we are interested in AND the line containing the process running the "ps -ef | \grep tcpdump | awk '{print $2}'" command - because it also contains "tcpdump".
So awk would then populate $pid with two pids.

The kill command afterwards would sucessfully kill the tcpdump process, but when it tries to kill the process that was running "ps -ef | blah blah blah" - kill will complain about trying to kill a non-existent process, because at that point the process running the "ps...." command would have already ended.

So the 2nd \grep filters out the "ps -ef | blah blah blah" line - preventing it's PID from being added to $pid and preventing an error message from kill.

One final consideration - if you have multiple tcpdump commands running, the above script would end up killing ALL running tcpdump processes.

If that is the case, and you only want to end ONE particular instance of tcpdump - you would need to another call to \grep to narrow in on the exact process you are looking for - based on some unique criteria that will identify it from the others.
e.g.
Perhaps it is the only one that is looking on wlan0 for udp packets? Or maybe the start-time of the process?

Either way - if there are several tcpdumps running - and you want to end a specific one - you will need to change the above script to \grep the output from ps for something that will uniquely identify the process you are after.

Hope this helps!
If you have any questions - fire away!
==================================================================================
But what you really want is something to get the PID of an already running tcpdump and kill it at a specified date/time. Is that correct?
- Yes, you are correct. I tried the script and it is now working, manually. Now, I need to schedule cron job and unfortunately seems my cron not working.

Like for example the tcpdump will start at 1AM and then it will stop terminate at 3AM in every Thursday. This is what I did.
00 03 * * 4 /bin/bash /home/server/scriptest.sh
 
Hmmm....The job-spec looks OK to me.

You can remove /bin/bash from the job-spec if your script has a shebang line in it.

e.g.
If the 1st line of scriptest.sh is something like:
Code:
#!/bin/bash

Or my preference:
Code:
#!/usr/bin/env bash

Using env in the shebang line to specify which interpreter the shell should use to interpret/execute a script avoids having a hard-coded path to the interpreter in your script.

So by using "#!/usr/bin/env bash" your script would work on systems where bash might be located somewhere else on the path. (e.g. /usr/bin/, or /usr/local/bin).

It doesn't just work for shellscripts. It also works for scripts written in python2, python3, perl, lua, LOLcode etc. etc.

At the start of any script - I use env in the shebang line:
Code:
#!/usr/bin/env {nameofinterpreter}

It's not a major thing. There is nothing wrong with specifying "/bin/bash" in your shellscripts if that's how your system is set up.
It's just something to consider for if you ever decide to use your scripts on another system.
It will save you from having to manually edit the shebang line for the script to match the current environment.

Getting back to your cron job - How did you create it?
Did you use crontab?:
Code:
sudo crontab -e

Or did you do something else?

Try using "sudo crontab -e" to open your cron config and try setting this for the job-spec :
Code:
0 3 * * 4 /home/server/scriptest.sh

Also, there are a few other things to check:
1. Is the scripts path/filename correct in the job-spec?
2. Are the permissions for scriptest.sh correct?
e.g.
Is the script executable?
Does the user running the script have the appropriate permissions?


Other than that, I can't see any obvious reason why the job wouldn't run.
 
No worries. Glad to have helped!

Hi JasKinasis, addition question please.

I need to know what's happening on the server every thursday between 1AM-2AM and I want it to zip the captured tcpdump. Hope to hear from you soon. Thank you in advance.

#!/bin/bash

pid=$(ps -ef | \grep tcpdump | \grep -v grep | awk '{print $2}')
if [[ $pid ]]; then
kill -15 $pid
fi
tcpdump -i eth0
 
What command are you using when you fire-up tcpdump?
Are you specifying a file to save to?
e.g.
Code:
tcpdump -i eth0 -w ~/eth0.pcap

If you fire up tcpdump at 1AM with something like the above. Then in your kill-script - after you have killed the process at 2AM, you could add a couple of lines to tar.gz the pcap file and delete the original, uncompressed file:
Code:
if [[ -f ~/eth0.pcap ]]; then
    tar -cvzf ~/eth0.pcap.tar.gz ~/eth0.pcap
    rm ~/eth0.pcap
fi

However, seeing as you want to start tcpdump at 1AM and kill it at 2AM, instead of using 2 scripts in cron - you could do something like my original post:
Code:
#!/usr/bin/env bash

tcpdump -w ~/eth0.pcap -i eth0 &
PROCESS_ID=$!
sleep 3600
kill -15 $PROCESS_ID
if [[ -f ~/eth0.pcap ]]; then
    tar -cvzf ~/eth0.pcap.tar.gz ~/eth0.pcap
    rm ~/eth0.pcap
fi
Save that as a script and run it as a cron job at 1AM on a Thursday.
That should run tcpdump for 1 hour, writing a .pcap file to the home folder of the user running the script - I assume root.
After an hour, the process is killed and the pcap file is .tar.gzipped and the uncompressed file deleted.

Then you can decompress and view the file when you get up on Friday morning.
 
What command are you using when you fire-up tcpdump?
Are you specifying a file to save to?
e.g.
Code:
tcpdump -i eth0 -w ~/eth0.pcap

If you fire up tcpdump at 1AM with something like the above. Then in your kill-script - after you have killed the process at 2AM, you could add a couple of lines to tar.gz the pcap file and delete the original, uncompressed file:
Code:
if [[ -f ~/eth0.pcap ]]; then
    tar -cvzf ~/eth0.pcap.tar.gz ~/eth0.pcap
    rm ~/eth0.pcap
fi

However, seeing as you want to start tcpdump at 1AM and kill it at 2AM, instead of using 2 scripts in cron - you could do something like my original post:
Code:
#!/usr/bin/env bash

tcpdump -w ~/eth0.pcap -i eth0 &
PROCESS_ID=$!
sleep 3600
kill -15 $PROCESS_ID
if [[ -f ~/eth0.pcap ]]; then
    tar -cvzf ~/eth0.pcap.tar.gz ~/eth0.pcap
    rm ~/eth0.pcap
fi
Save that as a script and run it as a cron job at 1AM on a Thursday.
That should run tcpdump for 1 hour, writing a .pcap file to the home folder of the user running the script - I assume root.
After an hour, the process is killed and the pcap file is .tar.gzipped and the uncompressed file deleted.

Then you can decompress and view the file when you get up on Friday morning.

====================================================================
Hi Jaskinakis,

I will be needing two scripts.

1. To start the tcpdump at 1AM
2. To stop the tcpdump at 2AM and zip the tcpdump logs (item #1).

With this, I would be able to know how the server behavior and get the info on the said time.

Thank you for your help! :)
 
====================================================================
Hi Jaskinakis,

I will be needing two scripts.

1. To start the tcpdump at 1AM
2. To stop the tcpdump at 2AM and zip the tcpdump logs (item #1).

With this, I would be able to know how the server behavior and get the info on the said time.

Thank you for your help! :)

No worries!
One script, or two scripts - the choice is yours. As the saying goes: there's more than one way to skin a cat!

Personally, I'd do it with one script. But that's just me.
My previous example script does it all in one.
It starts tcpdump in the background and stores it's Pid and then the script sleeps for 1hr - leaving tcpdump to do its thing and log traffic. Upon waking, it kills tcpdump and zips the log.... Set that up as a Cron job at 1AM on Thursday - Blam! Job done!

The advantage of the single script is that it keeps track of the pid of the instance of tcpdump that it started. So if there were ever multiple tcpdump processes running, the single script method will only kill the instance it started. It's self contained.

Whereas the two script method - using the kill script from my 2nd post - could potentially kill ALL running tcpdump's, (unless you added a grep command that would allow you to narrow in on the exact instance you are after.)

If you'll only ever have one tcpdump running, this isn't an issue. But as an example - if you decided to run an extra tcpdump on wlan0 at 1.30am on Thursday (until say...3am) alongside your regular 1.00am tcpdump on eth0, then your kill script at 2am could potentially kill both tcpdumps.

Whereas if the single script method was used - each script would clean up after itself and take care of killing the tcpdump instance it started.

That's the approach I'd take.

In fact I'd probably go a step further and make the script even more generic by allowing it to take some parameters, like the amount of time to run for, the interface to monitor and the filename for the log.

Then I wouldn't have to create a separate script for the hypothetical 1.30am check of wlan0.

With the script set up to take parameters:
In order to set up the two tcpdumps - I'd be able to do something like this in cron...
Code:
0 1 * * 4 /path/to/tcpscript eth0 60 /path/to/1amEth0.pcap
30 1 * * 4 /path/to/tcpscript wlan0 90 /path/to/130amWlan0.pcap
So by adding the ability to deal with parameters, I could set up as many scans as I wanted in cron by calling the script at different times and passing different parameters.... And each instance of the script will only ever kill the tcpdump instance it started....

If that makes sense?!

It's currently 1.30am, so I need to get my head down (and I'm on my phone), so I won't try to write a generic version of the script now.

And yet again - if you're only ever going to have to deal with a single instance of tcpdump - then ignore everything I've just said. You don't need to worry about it.

But it's something to consider if you think you might need additional scans ran on different interfaces, or different types of traffic at different times.

This is the sort of thing I consider all the time when writing scripts.

Basically, I like to be lazy. If I write a script, I try to make it as generic and useful as possible. The less scripts I have to write - the better! XD
 


Top