1

I have a bug in my while loop check. I am checking if the user input is a valid user interface. This is my code:

#!/bin/bash

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

# Network Interface selection

printf "\nPlease select the network interface you want to use:\n"
read -r user_iface

while ! [[ "${net_array[@]}" =~ $user_iface ]]; do # check if the user input is valid
        echo "Please enter a valid network interface:"
        read -r user_iface
done

Generally this code works and checks if the element is in the array. My PC has eno1, eno2, eno3 interfaces and when I insert something different like eno5 it asks me again to insert the network interface.

The problem is that if I insert only 1, it accepts it as a valid network interface, which is not the case and I want to exclude it. I guess I can perform additional check excluding all numerical user inputs, but I am wondering what my error is?

1 Answers1

8

The regex match [[ string =~ pattern ]] doesn't actually match against the full string, but searches for the pattern in the string (like grep). You'd need to lock the pattern to the beginning and end of line, with the ^ and $ special characters ("anchors").

So, you could do

[[ "$a" =~ ^"$b"$ ]]

quoting the variable so that its contents are not taken as a regex.

Though, since you only want to find full matches, why use a regex match at all, just compare for equality:

[[ "$a" = "$b" ]]

Except that of course your left hand side wasn't a single array item, but the whole array. Finding a matching in item an array with a pattern match like that is somewhat tricky, it was discussed here Using case and arrays together in bash (in relation to case, but a pattern match anyway).

In brief, you could do something like this, searching for the chosen item with a space or start/end of string on both sides:

[[ "${net_array[*]}" =~ (^| )"$user_iface"( |$) ]]

It should work as long as the user doesn't enter any whitespace, though we could of course change IFS to get another separator.


But I would just loop over the array elements and check for a match:

found=0
for a in "${net_array[@]}" ; do
     [[ "$a" = "$user_iface" ]] && found=1
done

Or put the loop in a function:

contains() {
        typeset _x;
        typeset -n _A="$1"
        for _x in "${_A[@]}" ; do
                [ "$_x" = "$2" ] && return 0
        done
        return 1
}

and then the whole asking for input thing would something like this

while read -p "please enter a network interface: " -r user_iface; 
      ! contains net_array "$user_iface" ; do
    echo "$user_iface is not a valid interface!"
done

... or just use select and save the users some typing:

select choice in "${net_array[@]}" ; do 
    if [[ "$choice" ]]; then break; else echo "invalid choice!"; fi;
done;
echo "you chose $choice";

(it loops until you break out)

ilkkachu
  • 138,973