我正在编写一个脚本来从源代码构建软件,并且有一个--platforms
选项。我想允许用户选择多个项目,但我不知道如何防止他们犯错误。
例子:
read -p "For what platforms do you wish to build [mac/win/linux32/linux64/all] ? "
if [[ -n "`echo $REPLY | grep 'win\|mac\|linux32\|linux64\|all`" ]] ; then
echo "ok"
else
echo "not ok"
fi
如果用户回答linux32
,应该没问题(确实如此)
如果用户回答linux32,mac
,应该没问题(确实如此)
如果用户回答lulz
,则应该不行(而且也确实不行)
如果用户回答linux32,lulz
,则应该不行(确实如此,这是我的问题)
我想知道您是否知道一种方法来允许用户输入他们想要的用逗号分隔的任何内容,但前提是它是脚本提供的选项之一,所以在这种情况下linux32 linux64 mac win all
。
也许有case
一种方法可以允许多个输入,或者添加一个elif $REPLY contains anything else than what we want
.另一个想法,可以awk
使用吗?我不知道自己该怎么做。
答案1
read
可以将输入分解为单词并将结果存储在数组中。将变量设置IFS
为单词分隔符(它必须是单个字符,而不是字符串 - 如果 的值IFS
包含多个字符,则每个字符都是单词分隔符)。
IFS=, read -a 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
您还可以一次性比较一组平台。如果您不想在检查代码中硬编码受支持的平台集,这会更方便。
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
另一种方法是使用内置函数一次提示一个平台select
。
1当然,如果您愿意,也可以在纯 bash 中执行此操作。
答案2
尝试这个!
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"
这将提示输入逗号分隔的选项列表。然后,它将将此列表拆分为一个数组,并遍历各个元素,分别检查每个元素,然后将所有“好”元素合并到一个变量中。
答案3
您可以使用读取单行输入sed
并将分隔符转换为换行符,例如:
% sed 'y/,/\n/;q' /dev/tty
> this,is,a,single,line
##OUTPUT
this
is
a
single
line
因为sed
将结果作为文本文件写入 stdout,所以使用 e x
plicit进行后续操作grep
很容易,并且发生在单个流中。事实上,如果你利用它的仪式文件功能,你就可以sed
像聪明人一样使用;而且,如果您写入文件描述符设备,您可以根据您定义的规则拆分其输出。tee
w
w
提示输入并仅将可接受参数的换行符分隔列表输出到 stdout 并将错误输出到 stderr 的函数可能如下所示:
_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
我把它分成希望更具描述性地命名辅助函数来代替注释,并将它们附加到主函数中。所以流程全部发生在前几行。我希望能把这一点说得更清楚。
_read_split
输出两个流 ->&1
和>&3
。_grep_ok
选取第一个$err
定义的行,并写入>&2
其输入中包含的所有不在_accept_prompt
的位置参数中的行。
_grep_ok
也同时选取第二个流 ->&3
并将其写入>&1 stdout
其输入中的所有行是其中_accept_prompt
的位置参数。
运行它:
% _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
您可以,
在调用时更改默认的逗号分隔符,例如:
delim=? _accept_prompt $args
答案4
read -p 'Enter a comma-separated list of platforms to build for [win/mac/linux32/linux64/all]: ' input
IFS=',' read -a options <<< "$input"
shopt -s extglob
for option in "${options[@]}"; do
case "$option" in
win|mac|linux@(32|64)|all)
buildcommand="${buildcommand:+$buildcommand,}$option"
buildvar=1;;
*)
printf 'Invalid option "%s" ignored.\n' "$option" >&2;;
esac
done
IFS=',' read -a options <<< "$buildcommand"
for option in "${options[@]}"; do
if [[ $option == 'all' ]]; then
buildcommand='all'
break
fi
done
if (( !buildvar )); then
echo 'Incorrect input. Default build selected.' >&2
buildcommand='default'
fi