ソースからソフトウェアをビルドするスクリプトを作成しており、--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
しません。 (はい、それは私の質問です。)
ユーザーが必要なものをカンマで区切って入力できるようにする方法を知っているかどうか疑問に思います。ただし、スクリプトが提供するオプションの1つである場合にのみ可能です。したがって、この場合はlinux32 linux64 mac win all
。
case
複数の入力を許可するか、1つを追加する方法がありますか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
別のアプローチは、組み込み機能を使用して一度に1つのプラットフォームを要求することです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
たとえば、次のように入力の1行を読み取り、sed
区切り文字を改行に変換できます。
% sed 'y/,/\n/;q' /dev/tty
> this,is,a,single,line
##OUTPUT
this
is
a
single
line
sed
結果はstdoutにテキストファイルとして書き込まれるため、eexplicitx
を使用した後続の操作は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
2つのストリームを出力します - >&1
sum >&3
。_grep_ok
最初に定義された行を取得し、位置引数にない入力に含まれるすべての行を$err
書き込みます。>&2
_accept_prompt
_grep_ok
また同時に2番目のストリームを選択して入力のすべての行>&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