Array output through a for loop problematic with multiple elements.

This code works perfect when using a machine with only one interface online. (Excluding the loopback of course) But when I have other interface up for vmware or a vpn the output gets mixed up. I know I had this working when I was just reading ip's from files so I know it is not a problem with nmap, even though it feeds back the error. Any help much appreciated.

#!/bin/bash

x=5.21

zero+=( "$(/sbin/ifconfig | grep "inet addr" | awk '{ print $2 }' | sed -e '/127.0.0.1/d' | cut -c6- | sed 's/.[^.]*$//' | uniq)" )

v=`nmap --version | grep "Nmap" | awk '{ print $3 }'`


for i in "${zero[@]}"
do
        if [ $v == $x ]; then
                one+=( "$(nmap -sP $i.0/24 | grep report | awk '{ print $5 }')" )
                sleep 1
        else
                one+=( "$(nmap -sP $i.0/24 | grep Host | awk '{ print $2 }')" )
                sleep 1
        fi
done

y=0
length=${#one[@]}
for (( z = 0; z < $length; z++ ));
do
    echo -e "${one[$y]}\n"
done

By the way. I have only tested this with nmap 5.00 and 5.21. It may not work at all on other versions.

replace your ip grabber line to :

ifconfig |grep `route -n |grep ^0.0.0.0 |awk '{print $2}' |sed 's/.[^.]*$//'` |awk -F":" '{print $2}' |awk '{print $1}' | sed 's/.[^.]*$//' |uniq

this will ignore all other interfaces.

Um... That's not what I want to do. I should have been more specific, but I'm trying to get all the interfaces (Minus loopback), store them in array zero, then output them one at a time through the loop. Otherwise I wouldn't have bothered making an array. But thanks for trying :slight_smile:

The following probably won't work as expected:

uniq requires that its input be sorted. The order of ip addresses generated by that command is likely to not be properly sorted. Even if it happens to be, it may not be guaranteed.

While it's harmless in this instance, the sed regular expression is probably more promiscuous than intended. The unescaped, leading dot is matching anything, not just a dot.

The grep|awk|sed|cut|sed|sort|uniq pipeline can be replaced by a single awk invocation:

awk '/inet addr/ && $2!="127.0.0.1" && !a[$2]++ {sub(/\.[^.]*$/, "", $2); print substr($2,6)}'

Although a single awk invocation is more efficient, unless a large amount of data is being processed, the performance gain is of no importance. Naturally, that awk will give incorrect results if the equivalent pipeline it replaces is itself incorrect.

The following is a clumsy way to iterate over a bash array.

Beyond that, it seems that y should be z or z should be y. y is never incremented in that loop. While the loop will execute once array member, it will invariably return the first the zeroth member. A simpler, more natural approach that would never have led to that bug:

for i in "${one[@]}";
do
    echo -e "$i\n"
done

Hopefully, something in this post is of some use to you. If not, provide us with more information such as your platform/operating system, sample output from the commands you're running (your ifconfig may not be the same as another's), and the problematic output versus the desired output ("the output gets mixed up." isn't at all helpful).

Regards,
Alister

Debian Linux (Squeeze & Sid)

This did not work. In particular:
$2!="127.0.0.1" && !a[$2]++ Which should be pretty cross platform.

# ./broken.sh
Invalid target host specification: 127.0.0
QUITTING!

I noticed what you said was true with the loop. I thought it was odd, but some site showed me to do it like that while outputting array elements. I changed everything to y and I piped through sed to remove 127.0.0.1 again and...

# ./broken.sh
Invalid target host specification: 192.168.12
QUITTING!
#!/bin/bash

x=5.21

zero+=( "$(/sbin/ifconfig |  sed -e '/127.0.0.1/d' | awk '/inet addr/ && $2!="127.0.0.1" && !a[$2]++ {sub(/\.[^.]*$/, "", $2); print substr($2,6)}')" )


v=`nmap --version | grep "Nmap" | awk '{ print $3 }'`


for i in "${zero[@]}"
do
        if [ $v == $x ]; then
                one+=( "$(nmap -sP $i.0/24 | grep report | awk '{ print $5 }')" )
                sleep 1
        else
                one+=( "$(nmap -sP $i.0/24 | grep Host | awk '{ print $2 }')" )
                sleep 1
        fi
done

y=0
length=${#one[@]}
for (( y = 0; y < $length; y++ ));
do
    echo -e "${one[$y]}\n"
done

Previously, I simply pointed out what stood out without giving the purpose of your code much thought. I seldom use linux (or bash), so after googling for a sample of linux's ifconfig output, here's how I'd do what I think you're trying to do:

#!/bin/sh

if [ 5.21 = $(nmap --version | awk '/Nmap/ { print $3 }') ]; then
    i=5  t=report    # field index and target text
else
    i=2  t=Host
fi

/sbin/ifconfig | awk '/inet addr/ && !/127.0.0.1/ && !a[$2]++ {print substr($2,6)}' |
    while read ip; do
          nmap -sP ${ip%.*}.0/24 | awk 'index($0,t) { print $i }' t="$t" i="$i"
          sleep 1
    done

The ifconfig|awk pipe feeding the while loop should output full ip addresses (all 4 octets) one per line. ${ip%.*}.0/24 strips the final octet and replaces it with 0 and the /24 cidr prefix.

If that doesn't work, then perhaps someone who has linux and nmap handy can help.

Regards,
Alister

---------- Post updated at 04:46 AM ---------- Previous update was at 04:32 AM ----------

In my first post, $2!="127.0.0.1" isn't eliminating the loopback address because it's trying to match an entire field whose value is that ip address. It didn't take into account that in the linux ifconfig output, the actual value of $2 would be addr:127.0.0.1 .

Regards,
Alister

1 Like

That works perfect until I try to output it from my array. (Which I dearly need).

#!/bin/bash

if [ 5.21 = $(nmap --version | awk '/Nmap/ { print $3 }') ]; then
    i=5  t=report    # field index and target text
else
    i=2  t=Host
fi

/sbin/ifconfig | awk '/inet addr/ && !/127.0.0.1/ && !a[$2]++ {print substr($2,6)}' |
    while read ip; do
          first+=( "$(nmap -sP ${ip%.*}.0/24 | awk 'index($0,t) { print $i }' t="$t" i="$i" )" )
          sleep 1
    done

echo "${first[@]}"

The above outputs absolutely nothing. Don't bother running it.

That's happening because bash runs the highlighted while-loop portion of the pipeline in a subshell. Any modification to $first, in that subshell, is not visible in the parent shell (where the echo happens).

Perhaps the third time is indeed the charm? ;):

#!/bin/bash

if [ 5.21 = $(nmap --version | awk '/Nmap/ { print $3 }') ]; then
    i=5  t=report    # field index and target text
else
    i=2  t=Host
fi

for ip in $(/sbin/ifconfig | awk '/inet addr/ && !/127.0.0.1/ && !a[$2]++ {print substr($2,6)}')
do
          first+=( "$(nmap -sP ${ip%.*}.0/24 | awk 'index($0,t) { print $i }' t="$t" i="$i" )" )
          sleep 1
done

echo "${first[@]}"

Regards,
Alister

1 Like

Yes! That did the trick! This helped me more than you'll know. Thank you very much!