2

I am trying to see if a user entered input is contained in a space delimited list.

interfaces=`ls /sys/class/net | awk '{ ORS=" "; print; }'`
# Loop until valid input for interface is received
while [[ -z "$interface" || ! "$interfaces" =~ "$interface" ]]
do
    echo -n "Select the interface ( "$interfaces"): "
    read interface
done

$interfaces may contain something along the lines of "eth0 lo wlan0 wlan1 " and I am trying to see if the user has entered an interface that is in that list, if not tell them to do it again.

I can't seem to figure out how to do this. I tried with wildcards and the == operator as well as regex matching with =~ but I haven't had much luck.

Is there a simple and clean way of checking to see if the user inputted value is within the list/string created by me?

Thanks for any help!

5
  • What is the problem you're seeing with your current version? Commented May 17, 2014 at 22:06
  • @thatotherguy Depending on if I am using wildcards or regex it either never exits the loop once valid input is entered, or it exits the loop no matter what input is entered. Currently with this code snippet it exits no matter the input. Commented May 17, 2014 at 22:07
  • 2
    Make sure you're testing the code you posted, and not a different version or a larger script where this is one part. It works for me, and asks until I enter something that's a substring of $interfaces. Commented May 17, 2014 at 22:17
  • FYI -- see mywiki.wooledge.org/ParsingLs; using ls programatically is never good practice. interfaces=( /sys/class/net/* ); interfaces=( "${interfaces[@]##*/}" ) is a better way to get an array of interface names. (Mind you, being an actual array rather than a space-separated string, the process for expanding it is a bit different) Commented May 17, 2014 at 22:18
  • ...or: interfaces=( /sys/class/net/* ); if [[ " ${interfaces[*]} " =~ " $foo " ]]; then ..., if you wanted to stick with string comparison for determining membership. Commented May 17, 2014 at 22:20

1 Answer 1

3

Update, thanks to feedback by @thatotherguy and @rici: Revised, after I realized I had a misconception about quoting the expression on the right side of =~ - see bottom:

The command in the question should work, but it matches the user input against any part of the list; here's a version that matches user input against whole list entries:

 while [[ -z $interface || ! " $interfaces " =~ " $interface " ]]

(As an aside: you could consider using bash's select command instead of the while loop, where the user is offered numbered choices and typing the desired number performs the selection.)


Re quoting on the right-hand side of =~ (applies to bash 3.2 or higher):

  • any quoted parts are treated as literals
  • unquoted parts are treated as regexes (i.e., characters such as ., *, ... have special meaning)

If the entire right-hand side is quoted - as in the solution above - =~ effectively performs literal substring matching.

Sign up to request clarification or add additional context in comments.

5 Comments

How would I modify the the right side of the conditional so that it matches for entire words. At the moment if I input "oii" it does ask for another input, but if I input "a" it exits which I believe is due to it matching to wlan0 in the list containing "eth0 lo wlan0 wlan1 ". The same effect can be seen if you enter "l", but if you enter "llll" it asks for a new input.
Why would you want to treat $interface as a regex and not a literal string in this case?
To add to what @thatotherguy said, if you don't quote $interface and the user types something like .*, then the regex match will produce an undesired result. +1 for the whitespace solution, though.
@rici Also a great point; hope my updated answer is now the right solution.
Upvoting this nice lecture on bash. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.