Check if a variable contains only what I want, and nothing else

read can break the input into words and store the result in an array. Set the IFS variable to the word separator character (it needs to be a single character, not a string — if the value of IFS contains multiple characters, then each character is a word separator).

IFS=, read -a platforms

Then check each element of the array against the set of supported platforms.

for p in "${platforms[@]}"; do
  case "$p" in
    win|mac|linux32|linux64) :;;
    all) platforms=(win mac linux32 linux64);;
    *) printf 1>&2 "Unsupported platform: %s\n" "$p"; return 2;;
  esac
done

You can also compare the set of platforms in one go. This is more convenient if you don't want to hardcode the set of supported platforms in the checking code¹.

supported_platforms=(win mac linux32 linux64)
IFS=, read -a platforms
bad_platform_names=($(comm -23 <(printf '%s\n' all "${platforms[@]}" | sort -u) \
                               <(printf '%s\n' "${supported_platforms[@]}" | sort -u)))
if [[ ${#bad_platform_names[@]} -ne 0 ]]; then
  printf "Unsupported platform: %s\n" "${bad_platform_names[@]}"
  exit 1
fi
if printf '%s\n' "${platforms[@]}" | grep -qx all; then
  platforms=("${supported_platforms[@]}")
fi

A different approach would be to prompt for platforms one at a time using the select builtin.

¹ Of course you can do this in pure bash if you prefer.


Try this!

buildvar=0
read -p "For what platforms do you wish to build [mac/win/linux32/linux64/all] ? " input
IFS=',' read -a options <<< "$input"

for option in "${options[@]}"
 do
  case "$option" in
    linux32)
        buildcommand="linux32" && buildvar=1
        ;;
    linux64)
        [ $buildvar == 1 ] && buildcommand="$buildcommand",linux64 || buildcommand="linux64" && buildvar=1
        ;;  
    mac)
        [ $buildvar == 1 ] && buildcommand="$buildcommand",mac || buildcommand="mac" && buildvar=1
        ;;  
    win)
        [ $buildvar == 1 ] && buildcommand="$buildcommand",win || buildcommand="win" && buildvar=1
        ;;
    all)
        buildcommand="all" && buildvar=1
        ;;
    *) 
        echo "'$option' was ignored." 
        ;;  
        esac
    done

[ $buildvar == "0" ] && echo "Incorrect input. Default build selected." && buildcommand="default"

echo "=> $buildcommand"

This will prompt for a comma separated list of options. It will then split this list into an array and iterate through the elements checking each element separately, then combine all "good" elements into a single variable.


You can read a single line of input with sed and translate the delimiter to newlines like:

% sed 'y/,/\n/;q' /dev/tty
> this,is,a,single,line
##OUTPUT
this
is
a
single
line

Because sed writes the result to stdout as a text file, following that up with an explicit grep is easy and occurs in a single stream. And, in fact, you can use sed like a smart tee if you make use of its write to file function; and, if you write to file descriptor devices, you can split its output based on rules you define.

A function that prompts for input and outputs only a newline delimited list of acceptable arguments to stdout and erroneous output to stderr might look like:

_accept_prompt() (
   . /dev/fd/0
   IFS=${delim-,}
   _prompt "$@" >&2
   { _read_split "$@" |
        err=all _grep_ok "$@" |
        sed '1{$d}' >&2
   } 3>&1 | _grep_ok "$@"
) <<\HELPERS

_prompt() {
    cat ; printf ' : '
} <<-PROMPT
    Choose from : $(printf "'%s' " "$@")
    Enter a '$IFS'-delimited selection below...
PROMPT

_read_split() {
    y="y/${IFS}/\n/"
    sed -ne "H;x;s/^/Invalid input IGNORED:/;${y};p;x" \
        -ne "/all/s/.*/$*/;${y};w /dev/fd/3" -ne q
} </dev/tty

_grep_ok() {
    grep -${err+v}xF "$(printf '%s\n' "$@" $err)"
}
HELPERS

I split this into hopefully more descriptively named helper functions in lieu of the comments and attached them to the main function. So the flow all happens in the first few lines. I hoped to make this clearer.

_read_split outputs two streams - >&1 and >&3. _grep_ok picks up the first with $err defined and writes to >&2 all lines contained in its input that are not among _accept_prompt's positional parameters.

_grep_ok also concurrently picks up the second stream - >&3 and writes to its >&1 stdout all lines in its input that are among _accept_prompt's positional parameters.

Running it:

% _accept_prompt this is the list of acceptable parameters
###PROMPT
    Choose from : 'this' 'is' 'the' 'list' 'of' 'acceptable' 'parameters'
    Enter a ','-delimited selection below...
###INPUT
 : all,invalid
###STDOUT
this
is
the
list
of
acceptable
parameters
###STDERR
Invalid input IGNORED:
invalid

You can alter the default ,comma delimiter on invocation like:

delim=? _accept_prompt $args

Tags:

Bash

Grep