変数に私が望むものだけが含まれており、他のものが含まれていないことを確認してください。

変数に私が望むものだけが含まれており、他のものが含まれていないことを確認してください。

ソースからソフトウェアをビルドするスクリプトを作成しており、--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 wantawkどうすればいいのかわかりません。

答え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記述子デバイスに書き込むことで、定義したルールに従って出力を分割できます。teeww

入力を要求し、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_split2つのストリームを出力します - >&1sum >&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

簡略化/改善されたバージョンanefm~の回答:

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

関連情報