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 ex
plicit 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 w
rite to file function; and, if you w
rite 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