比如,我有一个脚本,用这行代码调用:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

或者这个:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

什么是公认的解析方式,以便在每种情况下(或两者的某种组合)$v、$f和$d都将设置为true,而$outFile将等于/fiz/someOtherFile?


当前回答

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;
    
    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done

此解决方案:

句柄-n arg和--name=arg允许在末尾使用参数如果有任何拼写错误,则显示正常错误兼容,不使用抨击可读,不需要在循环中维护状态

其他回答

使用bash模块中的模块“参数”

例子:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"

我发现在脚本中编写可移植解析的问题非常令人沮丧,因此我编写了Argbash-一个FOSS代码生成器,它可以为您的脚本生成参数解析代码,并且具有一些不错的功能:

https://argbash.io

我想分享我为解析选项所做的工作。这里的答案没有满足我的一些需求,所以我不得不提出这个问题:https://github.com/MihirLuthra/bash_option_parser

这支持:

子选项分析选项的别名可选参数变量参数打印用法和错误

假设我们有一个名为fruit的命令,用法如下:

fruit <fruit-name> ...
   [-e|—-eat|—-chew]
   [-c|--cut <how> <why>]
   <command> [<args>] 

-e不带参数-c采用两个参数,即如何切割和为什么切割水果本身至少需要一个论点。<command>用于子选项,如apple、orange等(类似于git,它有子选项commit、push等)

所以要解析它:

parse_options \
    'fruit'                         '1 ...'  \
    '-e'     , '--eat' , '--chew'   '0'      \
    '-c'     , '--cut'              '1 1'    \
    'apple'                         'S'      \
    'orange'                        'S'      \
    ';' \
    "$@"

现在,如果出现任何使用错误,可以使用option_parser_error_msg打印,如下所示:

retval=$?

if [ $retval -ne 0 ]; then
    # this will manage error messages if
    # insufficient or extra args are supplied

    option_parser_error_msg "$retval"

    # This will print the usage
    print_usage 'fruit'
    exit 1
fi

现在检查是否通过了一些选项,

if [ -n "${OPTIONS[-c]}" ]
then
    echo "-c was passed"

    # args can be accessed in a 2D-array-like format
    echo "Arg1 to -c = ${ARGS[-c,0]}"
    echo "Arg2 to -c = ${ARGS[-c,1]}"

fi

子选项解析也可以通过将$shift_count传递给parse_options_detailed来完成,这使得它在移动args以到达子选项的args之后开始解析。这在本例中进行了演示。

自述文件和示例中提供了详细描述在存储库中。

这也可能很有用:您可以设置一个值,如果有人提供输入,则使用该值覆盖默认值。

myscript.sh-f/serverlist.txt或只是/myscript.sh(它采用默认值)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift
    
    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done

    
    echo "SERVER LIST   = ${SERVER_FILE_LIST}"

当我尝试这个问题时,上面的答案似乎有点bug——这是我发现更强大的解决方案:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi