how ping address from dynamic variable? - bash

I want to ping in a for loop using dynamic names.
When i try ping -c1 -w1 10.0.0.10 > /dev/null it works perfectly, but when i try to change my static adress into a dynamic one, i'll get an error.
declare -A array=([piotr_pc]=10.0.0.10 )
for item in ${!array[*]}
do
file=$(grep $item homesystem/web/openvpn-status.log | tr 'm\n' 'p,')
IFS=','
for x in $file
do
eval $item+=\("$x"\)
done
eval echo \${$item[9]}
if ping -c1 -w1 eval echo \${$item[9]} > /dev/null; then
echo eval echo \${$item[9]} "ONLINE" $NOW
echo "UPDATE openvpn SET status='ONLINE', last_online='$NOW' , Common Name = '${piotr_pc[0]}', Real Address = '${piotr_pc[1]}', Bytes Received = '${piotr_pc[2]}', Bytes Sent = '${piotr_pc[3]}', Connected Since = '${piotr_pc[4]}', Virtual Address = '${piotr_pc[5]}' , Last Ref = '${piotr_pc[8]}' WHERE ip_vpn='${piotr_pc[5]}'"
#mysql -uphptest -pphphaslo openwrt -e "UPDATE openvpn SET status='ONLINE', last_online='$NOW' WHERE ip_vpn='$ip'"
else
eval echo \${$item[9]} "OFFLINE"
#mysql -uphptest -pphphaslo openwrt -e "UPDATE openvpn SET status='OFFLINE' WHERE ip_vpn='$ip'"
fi
done
Result:
root#VigoradoNetwork:/www1# ./ping.sh
10.0.0.10
BusyBox v1.28.3 () multi-call binary.
Usage: ping [OPTIONS] HOST
Send ICMP ECHO_REQUEST packets to network hosts
-4,-6 Force IP or IPv6 name resolution
-c CNT Send only CNT pings
-s SIZE Send SIZE data bytes in packets (default 56)
-t TTL Set TTL
-I IFACE/IP Source interface or IP address
-W SEC Seconds to wait for the first response (default 10)
(after all -c CNT packets are sent)
-w SEC Seconds until ping exits (default:infinite)
(can exit earlier with -c CNT)
-q Quiet, only display output at start
and when finished
-p HEXBYTE Pattern to use for payload
10.0.0.10 OFFLINE
I want to change this 10.0.0.10 with eval echo \${$item[9]} but i dont know how to do this.

I'm not sure if i understood you correctly but following may be helpful if you are running multiple pings to hosts using a hostlist file.
#!/bin/bash
echo "`date` Starting the Ping Check...."
function pingHost () {
hostTarget=${1}
# send stdout/stderr to /dev/null since all we need is the return code
ping -c2 ${hostTarget} >/dev/null 2>&1 &&
echo Host ${hostTarget} is SUCCESS||
echo Host ${hostTarget} is FAILED
}
data=$(<my_hostlist) # Place your hostlist here example "Master_hostlist"
for line in ${data}
do
# call our function and place the call in the background
pingHost ${line} &
done
# wait for all outstanding background jobs to complete before continuing
wait
# [optional] let operator know we're done.
echo "Completed #=> `date`"

Related

Looping over IP addresses from a file using bash array

I have a file in which I have given all the IP addresses. The file looks like following:
[asad.javed#tarts16 ~]#cat file.txt
10.171.0.201
10.171.0.202
10.171.0.203
10.171.0.204
10.171.0.205
10.171.0.206
10.171.0.207
10.171.0.208
I have been trying to loop over the IP addresses by doing the following:
launch_sipp () {
readarray -t sipps < file.txt
for i in "${!sipps[#]}";do
ip1=(${sipps[i]})
echo $ip1
sip=(${i[#]})
echo $sip
done
But when I try to access the array I get only the last IP address which is 10.171.0.208. This is how I am trying to access in the same function launch_sipp():
local sipp=$1
echo $sipp
Ip=(${ip1[*]})
echo $Ip
Currently I have IP addresses in the same script and I have other functions that are using those IPs:
launch_tarts () {
local tart=$1
local ip=${ip[tart]}
echo " ---- Launching Tart $1 ---- "
sshpass -p "tart123" ssh -Y -X -L 5900:$ip:5901 tarts#$ip <<EOF1
export DISPLAY=:1
gnome-terminal -e "bash -c \"pwd; cd /home/tarts; pwd; ./launch_tarts.sh exec bash\""
exit
EOF1
}
kill_tarts () {
local tart=$1
local ip=${ip[tart]}
echo " ---- Killing Tart $1 ---- "
sshpass -p "tart123" ssh -tt -o StrictHostKeyChecking=no tarts#$ip <<EOF1
. ./tartsenvironfile.8.1.1.0
nohup yes | kill_tarts mcgdrv &
nohup yes | kill_tarts server &
pkill -f traf
pkill -f terminal-server
exit
EOF1
}
ip[1]=10.171.0.10
ip[2]=10.171.0.11
ip[3]=10.171.0.12
ip[4]=10.171.0.13
ip[5]=10.171.0.14
case $1 in
kill) function=kill_tarts;;
launch) function=launch_tarts;;
*) exit 1;;
esac
shift
for ((tart=1; tart<=$1; tart++)); do
($function $tart) &
ips=(${ip[tart]})
tarts+=(${tart[#]})
done
wait
How can I use different list of IPs for a function created for different purpose from a file?
How about using GNU parallel? It's an incredibly powerful wonderful-to-know very popular free linux tool, easy to install.
Firstly, here's a basic parallel tool usage ex.:
$ parallel echo {} :::: list_of_ips.txt
# The four colons function as file input syntax.†
10.171.0.202
10.171.0.201
10.171.0.203
10.171.0.204
10.171.0.205
10.171.0.206
10.171.0.207
10.171.0.208
†(Specific to parallel; see parallel usage cheatsheet here]).
But you can replace echo with just about any as complex series of commands as you can imagine / calls to other scripts. parallel loops through the input it receives and performs (in parallel) the same operation on each input.
More specific to your question, you could replace echo simply with a command call to your script
Now you would no longer need to handle any looping through ip's itself, and instead be written designed for just a single IP input. parallel will handle running the program in parallel (you can custom set the number of concurrent jobs with option -j n for any int 'n')* .
*By default parallel sets the number of jobs to the number of vCPUs it automatically determines your machine has available.
$ parallel process_ip.sh :::: list_of_ips.txt
In pure Bash:
#!/bin/bash
while read ip; do
echo "$ip"
# ...
done < file.txt
Or in parallel:
#!/bin/bash
while read ip; do
(
sleep "0.$RANDOM" # random execution time
echo "$ip"
# ...
) &
done < file.txt
wait

shell script to find dynamic public ip address in ubuntu does not show any output

I wrote the following script to find my dynamic public IP address and save how often it is changes
#!/usr/bin/env bash
ip=0
change=0
for ((count = 10000; count != 0, change == 10; count--)); do
fetch="$(dig +short myip.opendns.com #resolver1.opendns.com)"
dig +short myip.opendns.com #resolver1.opendns.com >>/home/nik/Desktop/file.txt
if [ $ip == 0 ]; then
ip=fetch
elif [ $ip != "$fetch" ]; then
change++
echo $ip
echo " changed to "
echo "$fetch"
echo " at "
echo date
else
echo ""
fi
echo "123"
sleep 13
(( count--))
done
I saved file as script.sh and changed it's executable permissions using
chmod +x script.sh
When I independently run dig command(in next line) or echo command directly in terminal, they log output to file without any problem
dig +short myip.opendns.com #resolver1.opendns.com>>/home/nik/Desktop/file.txt
but when I run the script, It shows no output nor does it log anything into text file.
I use Ubuntu 19.10 if it matters.
Edit: added shebang and changed wait to sleep
You have change=0 in the beginning of your file, and then depend on change == 10 in the conditional expression of your for loop.
I think you should review your code first :-)
A good place to start with a script that tracks public IP Address changes might be this guy:
#!/usr/bin/env bash
CURRENT_IP="$(timeout 5 dig +short myip.opendns.com #resolver1.opendns.com 2>/dev/null)"
num_changes=0
while [ 1 ]
do
NEW_IP="$(timeout 5 dig +short myip.opendns.com #resolver1.opendns.com 2>/dev/null)"
if echo "${CURRENT_IP}" | grep -q "${NEW_IP}"
then
echo "IP is the same" > /dev/null
else
let num_changes++
echo "${num_changes}: ${CURRENT_IP} -> ${NEW_IP}"
CURRENT_IP="${NEW_IP}"
fi
done
There are two variables being used, CURRENT_IP and NEW_IP
They are both being updated the same way timeout 5 dig +short ... 2>/dev/null
The timeout serves to ensure our script never hangs forever
The 2>/dev/null serves to filter error messages away
The num_changes variable keeps track of the number of times the IP changed
The only time this script will ever print any message at all is when your address changes
Example output: [NUM_CHANGES]: [LAST ADDRESS] -> [NEW ADDRESS]
1: 75.72.13.89 -> 74.76.77.88
2: 75.72.13.88 -> 74.76.77.87
3: 75.72.13.87 -> 74.76.77.86

fping hosts in file and return down ips

I want to use fping to ping multiple ips contained in a file and output the failed ips into a file i.e.
hosts.txt
8.8.8.8
8.8.4.4
1.1.1.1
ping.sh
#!/bin/bash
HOSTS="/tmp/hosts.txt"
fping -q -c 2 < $HOSTS
if ip down
echo ip > /tmp/down.log
fi
So I would like to end up with 1.1.1.1 in the down.log file
It seems that parsing the data from fping is somewhat difficult. It allows the parsing of data for hosts that is alive but not dead. As a way round the issue and to allow for multiple host processing simultaneously with -f, all the hosts that are alive are placed in a variable called alive and then the hosts in the /tmp/hosts.txt file are looped through and grepped against the variable alive to decipher whether the host is alive or dead. A return code of 1 signifies that grep cannot find the host in alive and hence an addition to down.log.
alive=$(fping -c 1 -f ipsfile | awk -F: '{ print $1 }')
while read line
do
grep -q -o $line <<<$alive
if [[ "$?" == "1" ]]
then
echo $line >> down.log
fi
done < /tmp/hosts.txt
Here's one way to get the result you want. Note however; i didn't use fping anywhere in my script. If the usage of fping is crucial to you then i might have missed the point entirely.
#!/bin/bash
HOSTS="/tmp/hosts.txt"
declare -i DELAY=$1 # Amount of time in seconds to wait for a packet
declare -i REPEAT=$2 # Amount of times to retry pinging upon failure
# Read HOSTS line by line
while read -r line; do
c=0
while [[ $c < $REPEAT ]]; do
# If pinging an address does not return the word "0 received", we assume the ping has succeeded
if [[ -z $(ping -q -c $REPEAT -W $DELAY $line | grep "0 received") ]]; then
echo "Attempt[$(( c + 1))] $line : Success"
break;
fi
echo "Attempt[$(( c + 1))] $line : Failed"
(( c++ ))
done
# If we failed the pinging of an address equal to the REPEAT count, we assume address is down
if [[ $c == $REPEAT ]]; then
echo "$line : Failed" >> /tmp/down.log # Log the failed address
fi
done < $HOSTS
Usage: ./script [delay] [repeatCount] -- 'delay' is the total amount of seconds we wait for a response from a ping, 'repeatCount' is how many times we retry pinging upon failure before deciding the address is down.
Here we are reading the /tmp/hosts.txt line by line and evaluating each adress using ping. If pinging an address succeeds, we move on to the next one. If an address fails, we try again for as many times as the user has specified. If the address fails all of the pings, we log it in our /tmp/down.log.
The conditions for checking whether a ping failed/succeeded may not be accurate for your use-cases, so maybe you will have to edit that. Still, i hope this gets the general idea across.

How can I throttle the network calls that some of my tmux plugins make?

I have a couple of tmux scripts, that display information like ip, time, hostname, etc in my tmux statusbar. For example, the ip script looks like this:
#!/bin/bash
runSegment() {
# check for a network connection
nc -z 8.8.8.8 53 >/dev/null 2>&1
online=$?
if [ $online -eq 0 ]; then
# get ip
ip=`curl icanhazip.com`
echo -n " ${ip}"
else
echo ""
fi
}
export -f runSegment
It checks for a network connection and gets the ip if there is one. Now my tmux statusbar is set to refresh every five seconds (tmux set-option -g status-interval 5). But making network requests to these services every five seconds seems a little excessive.
However, I'd like to keep stuff like battery status and time updated every five seconds, so setting the status-interval to five minutes or so is not an option.
So how do I get this script to return a cached value, and only after five minutes or so refresh that value? I'm assuming I need to solve this in bash, but I'd need to have internal state for that, and as this script gets run anew every time I'm not sure how to go about it.
So this works:
#!/bin/bash
runSegment() {
# check if online and assign exit code to variable
nc -z 8.8.8.8 53 >/dev/null 2>&1
local online=$?
if [ $online -eq 0 ]; then
# check how many seconds ago ip was retrieved
local lastmod=$(( $(date +%s) - $(stat -f%c ~/.current-ip) ))
# if longer than five minutes ago refresh the value and echo that
if [ $lastmod -gt 300 ]; then
local ip=$(curl icanhazip.com)
echo ${ip} > $HOME/.current-ip
echo -n " ${ip}"
# otherwise use the cached value
else
local ip=$(cat $HOME/.current-ip)
echo -n " ${ip}"
fi
# return empty value if there's no connection
else
echo ""
fi
}
export -f runSegment

Shell script port scanner

I would like to scan multiple ports in multiple hosts. I used this script but it takes long time to show the result.
#!/bin/bash
hosts=(
"server1"
"server2"
)
for host in "${hosts[#]}"
do
echo "=========================================="
echo "Scanning $host"
echo "=========================================="
for port in {21,22,80}
do
echo "" > /dev/tcp/$host/$port && echo "Port $port is open"
done 2>/dev/null
done
Some people suggested to use telnet or NetCat instead but i prefer to do it without installing any new packages. So, are there any ways to speed it up by multithreading or other way.
You could use GNU Parallel to run all the checks in parallel. I am not the best at using it, and #OleTange (the author) normally has to correct me but I keep trying. So, let's try your case, by building up to it slowly:
parallel echo {1} {2} ::: 192.168.0.1 192.168.0.8 ::: 21 22 80
192.168.0.8 22
192.168.0.8 80
192.168.0.8 21
192.168.0.1 80
192.168.0.1 22
192.168.0.1 21
looks kind of hopeful to me. Then I add in -k to keep the results in order, and I supply a function that takes those IP addresses and ports as arguments:
parallel -k 'echo "" > /dev/tcp/{1}/{2} && echo {1}:{2} is open' ::: 192.168.0.1 192.168.0.8 ::: 21 22 80 2>/dev/null
192.168.0.1:80 is open
192.168.0.8:21 is open
192.168.0.8:22 is open
192.168.0.8:80 is open
This will run 8 jobs in parallel if your CPU has 8 cores, however echo is not very resource intensive so you can probably run 32 in parallel, so add -j 32 after the -k.
If you wanted to stick closer to your own script, you can do it like this:
#!/bin/bash
hosts=(
"192.168.0.1"
"192.168.0.8"
)
for host in "${hosts[#]}"
do
for port in {21,22,80}
do
echo "(echo > /dev/tcp/$host/$port) 2>/dev/null && echo Host:$host Port:$port is open"
done
done | parallel -k -j 32
Basically, instead of running your commands, I am just sending them to the stdin of parallel so it can do its magic with them.
You could run all three pokes in the background, then wait for them all to finish, and probably slash the running time to 1/3.
for port in 21 22 80; do
echo "" > /dev/tcp/$host/$port 2>/dev/null &
pid[$port]=$!
done
for port in 21 22 80; do
wait $pid[$port] && echo "Port $port" is open"
done
You could add parallelism by running multiple hosts in the background, too, but that should be an obvious extension.
#!/bin/bash
function alarm {
local timeout=$1; shift;
# execute command, store PID
bash -c "$#" &
local pid=$!
# sleep for $timeout seconds, then attempt to kill PID
{
sleep "$timeout"
kill $pid 2> /dev/null
} &
wait $pid 2> /dev/null
return $?
}
function scan {
if [[ -z $1 || -z $2 ]]; then
echo "Usage: ./scanner <host> <port, ports, or port-range>"
echo "Example: ./scanner google.com 79-81"
return
fi
local host=$1
local ports=()
# store user-provided ports in array
case $2 in
*-*)
IFS=- read start end <<< "$2"
for ((port=start; port <= end; port++)); do
ports+=($port)
done
;;
*,*)
IFS=, read -ra ports <<< "$2"
;;
*)
ports+=($2)
;;
esac
# attempt to write to each port, print open if successful, closed if not
for port in "${ports[#]}"; do
alarm 1 "echo >/dev/tcp/$host/$port" &&
echo "$port/tcp open" ||
echo "$port/tcp closed"
done
}
scan $1 $2

Resources