检查变量是否只包含我想要的内容,而没有其他内容

检查变量是否只包含我想要的内容,而没有其他内容

我正在编写一个脚本来从源代码构建软件,并且有一个--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 xplicit进行后续操作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_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

相关内容