If i wanted to extract the ttl and display it from the ping command how could i go about doing that? - bash

Scripting and want to ping devices on a network, tell me if it's reachable or not, and then get the ttl data from the ping and tell me the operating system.
Ive tried using the awk command, but I am also new to scripting and may not be using it correctly.
for host in $(seq 1 255);
do
ping -c 1 $sn.$host | grep "Unreachable" &>/dev/null
if [ $? -eq 0 ]; then
printf "%s\n" "$sn.$host is Offline"
fi
ping -c 1 $sn.$host | grep "ttl" &>/dev/null
if [ $? -eq 0 ]; then
printf "%s\n" "$sn.$host is Online"
fi
done
I need the ttl value and to store it into a variable and then tell me what operating system it is based on Linux has a ttl of 64, windows a ttl of 128, and ios of 255

You can do things in a bit more concise manner and minimize the time waiting for an Offline host by setting a timeout using the -w (or -W) option. For example you can save the ttl=XX value from ping in the same call that determines whether the host is online or not and then you can use a simple parameter expansion to extract the numeric ttl value from the right side of the equal sign, e.g.
ttlstr=$(ping -c1 -w1 $sn.$host | grep -o 'ttl=[0-9][0-9]*')
Above the command substitution $(...) executes ping and pipes the output to grep and assigns the results to ttlstr. The command substitution return is the return of the last command in the pipeline telling you whether grep for "ttl=####" succeeded or failed. That's all you need to determine whether the host is online or not. On failure output your "Offline" message and try the next, e.g.
## ping with 1 sec timeout store ttl=xx in ttlstr
ttlstr=$(ping -c1 -w1 $sn.$host | grep -o 'ttl=[0-9][0-9]*') || {
printf "%s is Offline\n" "$sn.$host"
continue;
}
If the command substitution succeeds, you can output your "Online" message and you can isolate the numeric ttl using a simple parameter expansion to remove all characters up to, and including, the '=' sign from the beginning of the string leaving only the numeric ttl, e.g.
ttl="${ttlstr#*=}" ## parameter expansion separating numeric ttl
printf "%s is Online, ttl=%d\n" "$sn.$host" "$ttl"
Putting it altogether you could do:
#!/bin/bash
sn=${1:-192.168.6}
for host in $(seq 1 255); do
## ping with 1 sec timeout store ttl=xx in ttlstr
ttlstr=$(ping -c1 -w1 $sn.$host | grep -o 'ttl=[0-9][0-9]*') || {
printf "%s is Offline\n" "$sn.$host"
continue;
}
ttl="${ttlstr#*=}" ## parameter expansion separating numeric ttl
printf "%s is Online, ttl=%d\n" "$sn.$host" "$ttl"
done
Example Use/Output
note: the sn is taken as the 1st argument to the program (using a default of 192.168.6 above)
$ bash ~/scr/utl/chksubnet.sh
<snip>
192.168.6.14 is Offline
192.168.6.15 is Offline
192.168.6.16 is Offline
192.168.6.17 is Online, ttl=64
192.168.6.18 is Offline
192.168.6.19 is Offline
<snip>
Look things over and let me know if you have further questions.

Here's a way of using awk to extract the ttl:
$ ping -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=53 time=48.575 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 48.575/48.575/48.575/0.000 ms
$ ping -c 1 8.8.8.8 | awk -F'[ =]' '/ttl/ {print $8 }'
53
The -F parameter tells awk what the field separator is. I've indicated spaces or the equals sign. The body of the awk script is written as pattern / command pairs. In the one line script, awk runs the print $8 command for any line that has ttl in line. The command tells awk to print the eigth field (remember, the -F indicated how to break the input line into fields. The /ttl/ pattern could be replaced with $7 == "ttl", too. This latter form is more accurate, since it would only match ttl if it appeared as the 7th field.
There are better more general implementations.
If you want to do this quickly, review the nmap utility. For example,
nmap -sn 192.168.1.0/24
Nmap aside, your program can be improved a bit by ping'ing the IP once and piping the results to awk, letting awk generate ALL the output.

Below is the script which you can use to find the Operating System name based on the ttl value if host is Online.
for host in $(seq 1 255);
do
ping -c 1 $sn.$host | grep "Unreachable" &>/dev/null
if [ $? -eq 0 ]
then
printf "%s\n" "$sn.$host is Offline"
continue
else
printf "%s\n" "$sn.$host is Online"
ttlValue=`ping -c 1 $sn.$host | grep ttl | awk '{print $6}'|awk -F'=' '{print $2}' &>/dev/null`
if [ $ttlValue -eq 64 ]
then
echo "Operating is Linux"
elif [ $ttlValue -eq 128 ]
then
echo "Operating is Windows"
else
echo "Operating is IOS"
fi
fi
done

Related

Bash idle session times greater than 15 min

I need help completing this. Trying to take user sessions sitting idle for greater than 15 minutes which aren't being kicked off by sshd_config and kill them. this is what I have to pull the sessions, how do I filter for greater than 15 minutes.
#!/bin/bash
IFS=$'\n'
for output in $(w | tr -s " " | cut -d" " -f1,5 | tail -n+3 | awk '{print $2}')
do
echo "$output \> 15:00"
done
If you are using Awk anyway, a shell loop is a clumsy antipattern. Awk already knows how to loop over lines; use it.
A serious complication is that the output from w is system-dependent and typically reformatted for human legibility.
tripleee$ w | head -n 4
8:16 up 37 days, 19:02, 17 users, load averages: 3.49 3.21 3.11
USER TTY FROM LOGIN# IDLE WHAT
tripleee console - 27Aug18 38days -
tripleee s003 - 27Aug18 38 ssh -t there screen -D -r
If yours looks similar, probably filter out anything where the IDLE field contains non-numeric information
w -h | awk '$5 ~ /[^0-9]/ || $5 > 15'
This prints the entire w output line. You might want to extract just the TTY field ({print $2} on my system) and figure out from there which session to kill.
A more fruitful approach on Linux-like systems is probably to examine the /proc filesystem.
You can try something like this …
for i in $(w --no-header | awk '{print $4}')
do
echo $i | grep days > /dev/null 2>&1
if [ $? == 0 ]
then
echo "greater that 15 mins"
fi
echo $i | grep min> /dev/null 2>&1
if [ $? == 0 ]
then
mins=$(echo $i | sed -e's/mins//g')
if [ $min -gt 15 ]
then
echo "Greater than 15 mins"
fi
fi
done
The tricky part is going to be figuring out what pid to kill.

Stop script if nc connetion succeded in bash

How can I stop my script if the connection with netcat was successful?
For example if Connection to 192.168.2.4 21 port [tcp/ftp] succeeded! I'm not sure what holds that string of text.
#!/bin/bash
#Find first 3 octets of the gateway and set it to a variable.
GW=$(route -n | grep 'UG[ \t]' | awk '{print $2}' | cut -c1-10)
#loop through 1 to 255 on the 4th octect
for octet4 in {1..255}
do
sleep .2
nc -w1 $GW$octet4 21
done
You can test for nc exit status.
Eg.:
nc -w1 $GW$octet4 21
[[ "$?" -eq 0 ]] && exit
If command nc succeeded and return zero exit status which is implicitly stored in $? shell variable, exit the script. Or use just break instead of exit if you want to just jump out of the loop.
You can use the return code from nc, then break when it is equal to 0. Here is an example script that iterates until it hits googles DNS server IP 8.8.8.8 then breaks.
#!/bin/bash
for i in {1..10}; do
sleep 1;
echo Trying 8.8.8.$i
nc -w1 8.8.8.$i 53
if [ $? == 0 ]; then
break
fi
done
Your script would look like this:
#!/bin/bash
#Find first 3 octets of the gateway and set it to a variable.
GW=$(route -n | grep 'UG[ \t]' | awk '{print $2}' | cut -c1-10)
#loop through 1 to 255 on the 4th octect
for octet4 in {1..255}
do
sleep .2
nc -w1 $GW$octet4 21
if [ $? == 0 ]
then
break
fi
done

get rid of integer expression expected in script

I know this has been asked many times here, but I have looked through all of the previous ones and still can't resolve it. This is just a simple script that checks for a running service and takes the action I define.
#!/bin/bash
SERVICE="$1"
RESULT=`ps -a | sed -n /${SERVICE}/p`
MEM=$(ps aux | sort -rk +4 | grep $1 | grep -v grep | awk '{print $4}' | awk 'NR == 1')
if [ "${RESULT:-null}" = null ]; then
echo "$1 is NOT running"
else
echo "$MEM"
fi
if [ "$MEM" -ge 1 ]; then
mailx -s "Alert: server needs to be checked" me#admins.com
fi
This is the error I get:
./check_service: line 15: [: 5.4: integer expression expected
If I take out the command for the MEM variable and run it outside the script it returns 5.4, which is what I would expect. In my script I have tried changing the "1" to a "1.0" since the output would always be in decimal format, but that did not help. I feel like I am missing something simple here.
Error is due to the fact that bash only supports integer mathematics and your script is comparing 5.4 with 1.
You can fix your script by using:
if [[ ${MEM%.*} -ge 1 ]]l then
mailx -s "Alert: server needs to be checked" me#admins.com
fi
${MEM%.*} will strip part after decimal point and since you're just comparing it with 1 there is no need to have decimal point in variable MEM.

Bash script checking cpu usage of specific process

First off, I'm new to this. I have some experience with windows scripting and apple script but not much with bash. What I'm trying to do is grab the PID and %CPU of a specific process. then compare the %CPU against a set number, and if it's higher, kill the process. I feel like I'm close, but now I'm getting the following error:
[[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
what am I doing wrong? here's my code so far:
#!/bin/bash
declare -i app_pid
declare -i app_cpu
declare -i cpu_limit
app_name="top"
cpu_limit="50"
app_pid=`ps aux | grep $app_name | grep -v grep | awk {'print $2'}`
app_cpu=`ps aux | grep $app_name | grep -v grep | awk {'print $3'}`
if [[ ! $app_cpu -gt $cpu_limit ]]; then
echo "crap"
else
echo "we're good"
fi
Obviously I'm going to replace the echos in the if/then statement but it's acting as if the statement is true regardless of what the cpu load actually is (I tested this by changing the -gt to -lt and it still echoed "crap"
Thank you for all the help. Oh, and this is on a OS X 10.7 if that is important.
I recommend taking a look at the facilities of ps to avoid multiple horrible things you do.
On my system (ps from procps on linux, GNU awk) I would do this:
ps -C "$app-name" -o pid=,pcpu= |
awk --assign maxcpu="$cpu_limit" '$2>maxcpu {print "crappy pid",$1}'
The problem is that bash can't handle decimals. You can just multiply them by 100 and work with plain integers instead:
#!/bin/bash
declare -i app_pid
declare -i app_cpu
declare -i cpu_limit
app_name="top"
cpu_limit="5000"
app_pid=`ps aux | grep $app_name | grep -v grep | awk {'print $2'}`
app_cpu=`ps aux | grep $app_name | grep -v grep | awk {'print $3*100'}`
if [[ $app_cpu -gt $cpu_limit ]]; then
echo "crap"
else
echo "we're good"
fi
Keep in mind that CPU percentage is a suboptimal measurement of application health. If you have two processes running infinite loops on a single core system, no other application of the same priority will ever go over 33%, even if they're trashing around.
#!/bin/sh
PROCESS="java"
PID=`pgrep $PROCESS | tail -n 1`
CPU=`top -b -p $PID -n 1 | tail -n 1 | awk '{print $9}'`
echo $CPU
I came up with this, using top and bc.
Use it by passing in ex: ./script apache2 50 # max 50%
If there are many PIDs matching your program argument, only one will be calculated, based on how top lists them. I could have extended the script by catching them all and avergaing the percentage or something, but this will have to do.
You can also pass in a number, ./script.sh 12345 50, which will force it to use an exact PID.
#!/bin/bash
# 1: ['command\ name' or PID number(,s)] 2: MAX_CPU_PERCENT
[[ $# -ne 2 ]] && exit 1
PID_NAMES=$1
# get all PIDS as nn,nn,nn
if [[ ! "$PID_NAMES" =~ ^[0-9,]+$ ]] ; then
PIDS=$(pgrep -d ',' -x $PID_NAMES)
else
PIDS=$PID_NAMES
fi
# echo "$PIDS $MAX_CPU"
MAX_CPU="$2"
MAX_CPU="$(echo "($MAX_CPU+0.5)/1" | bc)"
LOOP=1
while [[ $LOOP -eq 1 ]] ; do
sleep 0.3s
# Depending on your 'top' version and OS you might have
# to change head and tail line-numbers
LINE="$(top -b -d 0 -n 1 -p $PIDS | head -n 8 \
| tail -n 1 | sed -r 's/[ ]+/,/g' | \
sed -r 's/^\,|\,$//')"
# If multiple processes in $PIDS, $LINE will only match\
# the most active process
CURR_PID=$(echo "$LINE" | cut -d ',' -f 1)
# calculate cpu limits
CURR_CPU_FLOAT=$(echo "$LINE"| cut -d ',' -f 9)
CURR_CPU=$(echo "($CURR_CPU_FLOAT+0.5)/1" | bc)
echo "PID $CURR_PID: $CURR_CPU""%"
if [[ $CURR_CPU -ge $MAX_CPU ]] ; then
echo "PID $CURR_PID ($PID_NAMES) went over $MAX_CPU""%"
echo "[[ $CURR_CPU""% -ge $MAX_CPU""% ]]"
LOOP=0
break
fi
done
echo "Stopped"
Erik, I used a modified version of your code to create a new script that does something similar. Hope you don't mind it.
A bash script to get the CPU usage by process
usage:
nohup ./check_proc bwengine 70 &
bwegnine is the process name we want to monitor 70 is to log only when the process is using over 70% of the CPU.
Check the logs at: /var/log/check_procs.log
The output should be like:
DATE | TOTAL CPU | CPU USAGE | Process details
Example:
03/12/14 17:11 |20.99|98| ProdPROXY-ProdProxyPA.tra
03/12/14 17:11 |20.99|100| ProdPROXY-ProdProxyPA.tra
Link to the full blog:
http://felipeferreira.net/?p=1453
It is also useful to have app_user information available to test whether the current user has the rights to kill/modify the running process. This information can be obtained along with the needed app_pid and app_cpu by using read eliminating the need for awk or any other 3rd party parser:
read app_user app_pid tmp_cpu stuff <<< \
$( ps aux | grep "$app_name" | grep -v "grep\|defunct\|${0##*/}" )
You can then get your app_cpu * 100 with:
app_cpu=$((${tmp_cpu%.*} * 100))
Note: Including defunct and ${0##*/} in grep -v prevents against multiple processes matching $app_name.
I use top to check some details. It provides a few more details like CPU time.
On Linux this would be:
top -b -n 1 | grep $app_name
On Mac, with its BSD version of top:
top -l 1 | grep $app_name

Ping hundreds in one script

I want to ping some servers on a game, they are all in the same format, only there are possibly hundreds of them. This is what I currently use:
ping server1.servername.com -n 20 | grep Minimum | awk '{print $3}' | sed s/,// >> Output.txt
That will ping the server 20 times, and chop off everything but the minimum ping amount. If I wanted to ping 300 of these servers, I would have to paste that same line 300 times... Is it possible to have it specify just something like 1-300 in one line without needing 300 lines of the same thing?
rojo#aspire:~$ help for
<snip...>
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
Arithmetic for loop.
Equivalent to
(( EXP1 ))
while (( EXP2 )); do
COMMANDS
(( EXP3 ))
done
EXP1, EXP2, and EXP3 are arithmetic expressions. If any expression is
omitted, it behaves as if it evaluates to 1.
Try something like this:
for (( x=1; $x<=300; x++ )); do ( ping server$x.servername.com -n 20 | grep Minimum | awk '{print $3}' | sed s/,// >> Output.txt ); done
Update:
Here's the hackish idea I mentioned in my comments to this answer below. Caveat: I think my ping command must be different from yours. I'm composing this idea on a Debian machine.
Instead of -n count my ping syntax is -c count, and instead of a line containing "Minimum" I have "min/avg/max/mdev". So you might need to play with the grep syntax and so on. Anyway, with that in mind, modify the following as needed to perform a ping of each server in sequence from 1 to whatever until error.
#!/bin/bash
i=0
while [ $? -eq 0 ] && i=$(( i + 1 )); do (
echo -n "server$i min: "
ping server$i.servername.com -c 20 -i 0.2 | grep -P -o -e '(?<=\= )\d\.\d+'
); done
echo "n/a"
Basically in plain English, that means while exit code = 0 and increment i, echo the server name without a line break and ping it 20 times at 200ms interval, completing the echoed line with (scraping from the ping results) a decimal number preceded by an equal-space. (That pattern matches the minimum ping time result in the summary for Linux iputils ping.) If the ping fails, exit code will not equal 0 and the loop will break.
You can use loops:
while read line
do
ping $line.servername.com -n 20 | grep Minimum | awk '{print $3}' | sed s/,// >> Output.txt
done < servers_list
Sounds like a job for xargs, e.g.,
$ cat server-list | xargs -I% ping % -n 20 ...

Resources