Bash手动输入和位置参数的移位

时间:2014-01-16 16:01:03

标签: bash function parameters shift getopts

(很明显,这个问题主要涉及如何将信息应用于getopts以及如何将shift应用于手动定义的数组。

我正在编写一个程序来启动一些Bash函数,这些函数以通常的getopts方式解析函数的参数,而解析意外的参数(通过多次循环getopts处理)。

在下面的代码中可以看到一个小函数process_arguments(),它基本上包含getopts处理和作用于位置参数的结束shift操作。因为参数解析的整个过程是针对函数而不是脚本,所以我想将函数process_arguments中包含的处理移动到主while循环中。剧本。这涉及修改函数中过程的输入和输出信息。

getopts处理的输入信息可以传递到数组getopts中的args

getopts optstring name [args]

但是,我不确定如何更改程序的输出。目前,它会移动位置参数,然后打印位置参数(echo "${@}")。

所以,问题......

  • getopts args是否以正确的方式将信息传递给程序?
  • 如何将shift应用于传递给getopts的参数,然后如何将这些已移位的参数输出到脚本处理的其余部分?

感谢您的帮助。我欢迎任何其他指导。总体目标是删除函数process_arguments()并将其功能合并到主``while```循环中。

#!/bin/bash
################################################################################
#
# This is a test of command line parameter parsing that is explicitly non-POSIX.
# This approach handles
#     - options (for getopts)
#     - arguments of options (for getopts)
#     - positional arguments (for getopts) and
#     - non-option arguments (command line parameters not expected by getopts).
# All non-option arguments are placed in the string non_option_parameters. The
# silent error reporting mode of getopts is required. Following command line
# parameter parsing, input information can be accepted based on internal
# priority assumptions.
#
# example usage:
#     ./script.sh 
#         non-option parameters:
#     ./script.sh  -a Dirac
#         -a triggered with parameter Dirac
#         non-option parameters: 
#     ./script.sh -a Dirac -b
#         -a triggered with parameter Dirac
#         -b triggered
#         non-option parameters: 
#     ./script.sh -a Dirac Feynman -b
#         -a triggered with parameter Dirac
#         -b triggered
#         non-option parameters: Feynman 
#     ./script.sh -a Dirac Feynman Born -b
#         -a triggered with parameter Dirac
#         -b triggered
#         non-option parameters: Feynman Born 
#     ./script.sh -a Dirac Feynman Born Born -b
#         -a triggered with parameter Dirac
#         -b triggered
#         non-option parameters: Feynman Born
#     ./script.sh -a Dirac Feynman Born Born -b
#         -a triggered with parameter Dirac
#         -b triggered
#         non-option parameters: Feynman Born Born
#
################################################################################

process_arguments(){
    OPTIND=1
    while getopts "${option_string}" option; do
        case ${option} in
            a)
                echo "-a triggered with parameter "${OPTARG}"" >&2 # output to STDERR
                ;;
            b)
                echo "-b triggered" >&2 # output to STDERR
                ;;
            \?)
                echo "invalid option: -"${OPTARG}"" >&2 # output to STDERR
            ;;
        esac
    done
    shift $(expr ${OPTIND} - 1)
    echo "${@}"
}

option_string=":a:b"
non_option_parameters=""
parameters="${@}"
while [ ! -z "${parameters}" ]; do
    parameters="$(process_arguments ${parameters})"
    non_option_parameters="${non_option_parameters} "$(awk -F '[ \t]*-' '{print $1}' <<< "${parameters}")
    parameters=$(awk -F '[ \t]*-' 'NF > 1{print substr($0, index($0, "-"))}' <<< "${parameters}")
done
echo "non-option parameters:${non_option_parameters}"

2 个答案:

答案 0 :(得分:1)

我没有花很多时间阅读你的代码。我同意你的看法:在脚本的“主要级别”处理你的选项,这样你就可以直接使用脚本的位置参数。

这是重写:

#!/bin/bash

while getopts ":a:b" option; do
    case $option in
        a)
            echo "-a triggered with parameter $OPTARG" >&2
            ;;
        b)
            echo "-b triggered" >&2
            ;;
        :)
            echo "missing argument for option -${OPTARG}" >&2
            ;;
        \?)
            echo "invalid option: -${OPTARG}" >&2
            ;;
    esac
done
shift $(( OPTIND - 1 ))

echo "remaining arguments:"
for (( i=1; i<=$#; i++ )); do
    printf "%d: '%s'\n" $i "${!i}"
done

行动中:

$ bash ./opts.sh -a A -b B -c -d "foo bar" baz
-a triggered with parameter A
-b triggered
remaining arguments:
1: 'B'
2: '-c'
3: '-d'
4: 'foo bar'
5: 'baz'

“-c”和“-d”没有看到任何“无效选项”错误,因为while getopts循环在达到第一个非选项参数“B”时结束。


更新:多次调用getopts:

extra_args=()

while (( $# > 0 )); do
    OPTIND=1
    while getopts ":a:b" option; do
        case $option in
            a) echo "-a triggered with parameter $OPTARG" >&2 ;;
            b) echo "-b triggered" >&2 # output to STDERR ;;
            :) echo "missing argument for option -$OPTARG" >&2 ;;
            ?) echo "invalid option: -$OPTARG" >&2 ;;
            esac
    done
    shift $(( OPTIND - 1 ))
    if (( $# > 0 )); then 
        extra_args+=( "$1" )
        shift
    fi
done

echo "non-option arguments:"
printf "%s\n" "${extra_args[@]}"
$ bash ~/tmp/opts.sh   -a A B -c -d "foo bar" baz -b
-a triggered with parameter A
invalid option: -c
invalid option: -d
-b triggered
non-option arguments:
B
foo bar
baz

答案 1 :(得分:1)

无移位解决方案如下。

我使用bash数组来保存位置(非标志)参数,因为它是处理具有空格的参数的唯一有效方法。特别是,尽管有引用,但echo "${@}"有效擦除了论据,因为echo不会保留引用。

我也保留process_argument,虽然您可以轻松地将其放入循环中。它差别很小(直接调用exit 1而不是return 1可以说更简单。)

# process_argument option
# Do whatever needs to be done to handle provided option. Fail on error.
OPTIONS=:a:b
process_argument() {
  case "$1" in
    a)
        echo "-a triggered with parameter $OPTARG" >&2
        ;;
    b)
        echo "-b triggered" >&2 # output to STDERR
        ;;
    :)
        echo "missing argument for option -${OPTARG}" >&2
        return 1
        ;;
    \?)
        echo "invalid option: -${OPTARG}" >&2
        return 1
        ;;
  esac
}

# Loop to handle all parameters
non_option_parameters=()
while true; do
  while getopts "$OPTIONS" option; do
    if ! process_argument "$option"; then exit 1; fi
  done
  if ((OPTIND > $#)); then break; fi
  non_option_parameters+=(${!OPTIND})
  ((OPTIND++))
done

printf "Non-option parameters: "
printf "<%s> " "${non_option_parameters[@]}"; echo