12

I want to create an array in Bash which to contain all active network interfaces.

I have managed to create a for loop printing them but when I try to create the array, it just contains the last lo interface but not the other. This is my code:

#!/bin/bash

for iface in $(ifconfig | cut -d ' ' -f1| tr ':' '\n' | awk NF)
do
           printf "$iface%s\n"
           declare -a array_test=["$iface"]
done
for i in "${array_test[@]}"; do echo "$i"; done

And this is my output:

eno1
eno2
eno3
lo
[lo]

Also, how can I exclude the lo localhost interface from the array?

2
  • 1
    @peterh bash can be used for quite complex scripts (as can, e.g., ksh or zsh)....but the more complex it is, the harder it is to read, understand, and modify - and the slower it becomes (e.g. a loop around forking several programs is abysmally slow). At that point, the time/effort cost of writing and maintaining a shell script greatly exceeds the cost of learning the basics of awk (or perl, or python) and rewriting it in about 1/10th the lines of codes. If you already know one of those languages, then that point is reached much earlier.
    – cas
    Commented Dec 23, 2017 at 6:42
  • I am writing a script to emulate network impairments using iproute and tc command. I was using this array approach to check if the user defined variable is valid. I believe writing this script in python would be a bit painful and I am also using the opportunity to learn a bit of bash. Commented Dec 23, 2017 at 7:43

5 Answers 5

13

Here is a solution, assign the list and then add item to it:

#!/bin/bash

array_test=()
for iface in $(ifconfig | cut -d ' ' -f1| tr ':' '\n' | awk NF)
do
        printf "$iface\n"
        array_test+=("$iface")
done
echo ${array_test[@]}

If you want the output displayed one item per line:

for i in "${array_test[@]}"; do echo "$i"; done

To remove localhost from output:

if [ "$iface" != "lo" ] 
then
    array_test+=("$iface")
fi
4
  • how can I exclude the lo localhost interface from the array? Commented Dec 22, 2017 at 14:38
  • added in answer and question Commented Dec 22, 2017 at 14:45
  • Actually you can do the same just by removing the last item in the array, I have found the answer in the meantime: unset "net_array[${#net_array[@]}-1]" Commented Dec 22, 2017 at 14:49
  • 1
    I won't do that because I'm not sure localhost will always be in last position Commented Dec 22, 2017 at 14:52
6

My try:

readarray -t interfaces < <(ip l | awk -F ":" '/^[0-9]+:/{dev=$2 ; if ( dev !~ /^ lo$/) {print $2}}')
for i in "${interfaces[@]// /}" ; do echo "$i" ; done
3

bash will construct an array from any white-space delimited (spaces, tabs, newlines) list you give it. e.g. array=(a b c). We can use command substitution ($()) to generate such a white-space delimited list. For example:

$ ifaces=( $(ip addr list | awk -F': ' '/^[0-9]/ {print $2}') )

and now print out the array we just created:

$ declare -p ifaces
declare -a ifaces=([0]="lo" [1]="eth0" [2]="eth1" [3]="br1" [4]="br0" [5]="ppp0")

To exclude lo:

$ ifaces=( $(ip addr list | awk -F': ' '/^[0-9]/ && $2 != "lo" {print $2}') )
$ declare -p ifaces
declare -a ifaces=([0]="eth0" [1]="eth1" [2]="br1" [3]="br0" [4]="ppp0")

If you really want to use ifconfig rather than ip, try this:

ifaces=( $(ifconfig | awk -F':'  '/^[^ ]*: / && $1 != "lo" {print $1}') )
3

ifconfig only outputs the interfaces that are up.

On Linux, you can get a list of interfaces in the /sys/class/net directory. So, with zsh, you can do:

interfaces=(/sys/class/net/*(DN:t))

To exclude lo:

set -o extendedglob
interfaces=(/sys/class/net/^lo(DN:t))

For only the up ones, you could do:

interfaces=(/sys/class/net/*(DNe{'[[ $(<$REPLY/operstate) = up ]]'}:t)

or, assuming the default value of $IFS.

interfaces=($(ip -br link | LC_ALL=C awk '$2 == "UP" {print $1}'))

On my system at least the status for lo is UNKNOWN so would be excluded.

That would also work with bash provided interface names don't contain wildcard characters. In recent versions of Linux, IIRC, interface names are guaranteed not to contain space tab and newline characters (the default $IFS), so those would not cause problem. Other Unicode blanks are allowed though, hence the LC_ALL=C which should guarantee that awk won't split on those.

1

If you have '-j' support for ip (json output) then use the excellent jq utility;

# ip -j addr show | jq -r .[].ifname
lo
eth0
eth1
tunl0
gre0
gretap0
erspan0
ip_vti0
ip6_vti0
sit0
ip6tnl0
ip6gre0
dummy0
kube-ipvs0
cbr0
vethfbcab624

(example from a kubernetes node)

1
  • 1
    Typo; shall be "ip -j link show"
    – lgekman
    Commented Nov 21, 2019 at 12:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .