Stop script if nc connetion succeded in bash - 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

Related

Can't verify result from SSH

I'm verifying matches of a file via SSH to a host ubunty system, and the if statement is not correctly processing the result.
export FILENAME=test.txt
export NUM=$(ssh -t ubuntu#192.168.XXX.XXX "ls ~/Documents/ | grep '$FILENAME' | wc -l")
echo "Received value: $NUM"
if [ $NUM == 0 ]; then
echo "If processed as: 0"
else
echo "If processed as: 1"
fi
So if $FILENAME exists, I get the following output
Connection to 192.168.XXX.XXX closed.
Received value: 1
If processed as: 1
And if not, I get the following one
Connection to 192.168.XXX.XXX closed.
Received value: 0
If processed as: 1
Why may this be happening? Am I getting a wrong formatted value? If I force before the if statement NUM=0 or NUM=1 it gets correctly processed.
if [ $NUM == 0 ]; then should work as expected. (More info on SO)
Use cat -v to show all invisible chars in your output;
NUM=$(ssh -t ubuntu#192.168.XXX.XXX "ls ~/Documents/ | grep '$FILENAME' | wc -l")
echo "NUM: ${NUM}" | cat -v
#Prints; NUM: 0^M
The invisible ^M char is messing with the if statement.
Remove if from the result by piping through tr -d '\r'
export FILENAME=test.txt
export NUM=$(ssh -t ubuntu#192.168.XXX.XXX "ls ~/Documents/ | grep '$FILENAME' | wc -l" | tr -d '\r')
echo "Received value: $NUM"
if [ $NUM == 0 ]; then
echo "If processed as: 0"
else
echo "If processed as: 1"
fi
More ^M info;
What is ^M
Remove ^M from variable

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

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

Ping Script with filter

I have a text file with host names and IP addresses like so (one IP and one host name per row). The IP addresses and host names can be separated by a spaces and/or a pipe, and the host name may be before or after the IP address
10.10.10.10 HW-DL11_C023
11.11.11.11 HW-DL11_C024
10.10.10.13 | HW-DL12_C023
11.11.11.12 | HW-DL12_C024
HW-DL13_C023 11.10.10.10
HW-DL13_C024 21.11.11.11
HW-DL14_C023 | 11.10.10.10
HW-DL14_C024 | 21.11.11.11
The script below should be able to ping hosts with a common denominator e.g. DL13 (there are two devices and it will ping only those two). What am I doing wrong, as I simply can`t make it work?
The script is in the same directory as the data; I don`t get errors, and everything is formatted. The server is Linux.
pingme () {
hostfile="/home/rex/u128789/hostfile.txt"
IFS= mapfile -t hosts < <(cat $hostfile)
for host in "${hosts[#]}"; do
match=$(echo "$host" | grep -o "\-$1_" | sed 's/-//' | sed 's/_//')
if [[ "$match" = "$1" ]]; then
hostname=$(echo "$host" | awk '{print $2}')
ping -c1 -W1 $(echo "$host" | awk '{print $1}') > /dev/null
if [[ $? = 0 ]]; then
echo "$hostname is alive"
elif [[ $? = 1 ]]; then
echo "$hostname is dead"
fi
fi
done
}
Try adding these two lines to your code:
pingme () {
hostfile="/home/rex/u128789/hostfile.txt"
IFS= mapfile -t hosts < <(cat $hostfile)
for host in "${hosts[#]}"; do
echo "Hostname: $host" # <-------- ADD THIS LINE -------
match=$(echo "$host" | grep -o "\-$1_" | sed 's/-//' | sed 's/_//')
echo "...matched with $match" # <-------- ADD THIS LINE -------
if [[ "$match" = "$1" ]]; then
hostname=$(echo "$host" | awk '{print $2}')
ping -c1 -W1 $(echo "$host" | awk '{print $1}') > /dev/null
if [[ $? = 0 ]]; then
echo "$hostname is alive"
elif [[ $? = 1 ]]; then
echo "$hostname is dead"
fi
fi
done
}
Then when you run it, you should see a list of your hosts, at least.
If you don't then you're not reading your file successfully.
If you do, there's a problem in your per-host logic.
Congratulations! You've divided your problem into two smaller problems. Once you know which half has the problem, keep dividing the problem in half until the smallest possible problem is staring you in the face. You'll probably know the solution at that point. If not, add your findings to the question and we'll help out from there.
The original code doesn't handle the pipe separator or the possibly reversed hostname and IP address in the input file. It also makes a lot of unnecessary use of external programs (grep, sed, ...).
Try this:
# Enable extended glob patterns - e.g. +(pattern-list)
shopt -s extglob
function pingme
{
local -r host_denom=$1
local -r hostfile=$HOME/u128789/hostfile.txt
local ipaddr host tmp
# (Add '|' to the usual characters in IFS)
while IFS=$'| \t\n' read -r ipaddr host ; do
# Swap host and IP address if necessary
if [[ $host == +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]] ; then
tmp=$host
host=$ipaddr
ipaddr=$tmp
fi
# Ping the host if its name contains the "denominator"
if [[ $host == *-"$host_denom"_* ]] ; then
if ping -c1 -W1 -- "$ipaddr" >/dev/null ; then
printf '%s is alive\n' "$host"
else
printf '%s is dead\n' "$host"
fi
fi
done < "$hostfile"
return 0
}
pingme DL13
The final line (call the pingme function) is just an example, but it's essential to make the code do something.
REX, you need to be more specific about your what IP's you are trying to get from this example. You also don't ping enough times IMO and your script is case sensitive checking the string (not major). Anyway,
First, check that your input and output is working correctly, in this example I'm just reading and printing, if this doesn't work fix permissions etc :
file="/tmp/hostfile.txt"
while IFS= read -r line ;do
echo $line
done < "${file}"
Next, instead of a function first try to make it work as a script, in this example I manually set "match" to DL13, then I read each line (like before) and (1) match on $match, if found I remove the '|', and then read the line into an array of 2. if the first array item is an a IP (contains periods) set it as the IP the other as hostname, else set the opposite. Then run the ping test.
# BASH4+ Example:
file="/tmp/hostfile.txt"
match="dl13"
while IFS= read -r line ;do
# -- check for matching string (e.g. dl13 --
[[ "${line,,}" =~ "${match,,}" ]] || continue
# -- We found a match, split out host/ip into vars --
line=$(echo ${line//|})
IFS=' ' read -r -a items <<< "$line"
if [[ "${items[0]}" =~ '.' ]] ;then
host="${items[1]}" ; ip="${items[0]}"
else
host="${items[0]}" ; ip="${items[1]}"
fi
# -- Ping test --
ping -q -c3 "${ip}" > /dev/null
if [ $? -eq 0 ] ;then
echo "$host is alive!"
else
echo "$host is toast!"
fi
done < "${file}"

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 ...

Error Handling on bash script

Infinite loop on bash script and I want to run forever but (I guess) something goes wrong script is killed. Is there any way like try-catch, just continue to running forever, unconditionaly.
#!/bin/bash
iteration=0
for (( ; ; ))
do
process_id=`ps -ef | grep java | grep TEST | awk '{print $2}' `
kill_command='kill -3 '$process_id
time=`date | awk '{print substr($4,0,5)}' `
last_write=`ls -l /files/*.txt | awk '{print $8}' `
if [ "$time" != "$last_write" ]
then
$kill_command
sleep 1
$kill_command
sleep 1
$kill_command
sleep 1
/test/show_queue.sh
fi
let "iteration+=1"
if [ "$iteration" == "30" ]
then
let "iteration=0"
$kill_command
echo '------------' >> memory_status.log
date >> memory_status.log
prstat -n 7 1 1 >> memory_status.log
echo '------------' >> memory_status.log
/test/show_queue.sh
fi
sleep 60
done
A very simple way to do it is to use two scripts. One with the loop and one which does the killing task :
for (( ; ; ))
do
DoKillingTask
rc=$? # <- You get the return code of the script and decide what to do
done
If it continues to be killed, Mikel (in comment of your question) is right.

Resources