I have a query.sh
script which runs dig
commands, performing a set of lookups from different DNS servers, which are currently given in a column in the single input file used (sites.txt
).
My goal is to modify this script to use a different input file, dns_servers.txt
, to find the DNS servers to iterate through for each query.
I'm not clear on where to start here. What do I need to do to be able to safely nest while read
loops?
#!/bin/sh
while read line;
do
set $line
echo "SITE:" $1@$2
/usr/sbin/dig +short -4 @$2 $1
sleep 5
exitStatus=$?
echo "Exit Status: " $exitStatus
done < sites.txt
Current format has a hostname and a DNS server to use for lookups against that hostname.
www.google.com 8.8.4.4
The intent is for the column with the DNS server to be ignored, and the contents of dns_servers.txt
to be used instead.
10.1.1.1
12.104.1.232
...
Nested loop definition That is to say, it is a loop that exists inside an outer loop. When you integrate nested loops in bash scripting, you are trying to make a command run inside another command. This is what it means: a bash nested for loop statement runs a command within another loop.
There are three basic loop constructs in Bash scripting, for loop, while loop , and until loop .
The bash while loop is a control flow statement that allows code or commands to be executed repeatedly based on a given condition. For example, run echo command 5 times or read text file line by line or evaluate the options passed on the command line for a script.
Ignoring any additional column(s) in the sites.txt
file, and iterating through the lines of dns_servers.txt
, might look like the following:
#!/bin/sh
while read -r site _ <&3; do
while read -r dns_server <&4; do
dig +short -4 "@$dns_server" "$site"; exit=$?
sleep 5
echo "Exit status: $exit"
done 4<dns_servers.txt
done 3<sites.txt
The key changes here:
read
. The underscore passed as second positional argument to the first read
is the variable name to which the second column of site.txt
is now being saved.3
and 4
) for outer and inner loops to keep them separated.Incidentally, if you were targeting bash (#!/bin/bash
) rather than POSIX sh (#!/bin/sh
), I might do this differently. The below uses the bash 4 extension mapfile
to read dns_servers.txt
all at once:
#!/bin/bash
readarray -t dns_servers <dns_servers.txt
while read -r site _; do
for from_ip in "${dns_servers[@]}"; do
dig +short -4 "@$from_ip" "$site"; exit=$?
sleep 5
echo "Exit status: $exit"
done
done <sites.txt
Here, we read dns_servers.txt
only once, and reuse that list of values for each value read from sites.txt
.
If you're using bash 3.x, mapfile
can be replaced with a loop:
dns_servers=( )
while read -r; do dns_servers+=( "$REPLY" ); done <dns_servers.txt
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With