Shell脚本:是否可以将getopts与位置参数混合?

SiB*_*SiB 49 parameters bash shell

我想设计一个shell脚本作为几个脚本的包装器.我想指定myshell.sh使用getopts的参数,并以相同的顺序将其余参数传递给指定的脚本.

如果myshell.sh执行如下:

myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3

myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh

myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3
Run Code Online (Sandbox Code Playgroud)

所有上述内容都应该可以作为

test.sh param1 param2 param3
Run Code Online (Sandbox Code Playgroud)

是否可以利用选项参数 myshell.sh 并将剩余参数发布到底层脚本?

小智 82

很抱歉评论了一个旧帖子,但我想我会发帖给那些正在搜索如何做到这一点的人...

我想做一些类似OP的事情,我找到了我在这里这里需要的相关信息

基本上如果你想做的事情如下:

script.sh [options] ARG1 ARG2
Run Code Online (Sandbox Code Playgroud)

然后得到这样的选项:

while getopts "h:u:p:d:" flag; do
case "$flag" in
    h) HOSTNAME=$OPTARG;;
    u) USERNAME=$OPTARG;;
    p) PASSWORD=$OPTARG;;
    d) DATABASE=$OPTARG;;
esac
done
Run Code Online (Sandbox Code Playgroud)

然后你可以得到这样的位置参数:

ARG1=${@:$OPTIND:1}
ARG2=${@:$OPTIND+1:1}
Run Code Online (Sandbox Code Playgroud)

更多信息和详细信息可通过上面的链接获得.

希望有所帮助!!

  • 但这不允许在标志之前的位置参数,这是OP问题的一部分.处理positionals/after/flags的另一种标准方法是在你的esac之后移位$((OPTIND-1))然后正常处理位置. (30认同)
  • 另一种常见模式是通过getopts移除所有args进程,然后以$ 1,$ 2处理重新执行的进程:`while getopts"h:u:p:d:"flag; 做....完成; shift $(($ OPTIND - 1)); echo $ 1; // Positional arg1 echo $ 2; //位置arg2` (2认同)

小智 14

myshell.sh:

#!/bin/bash

script_args=()
while [ $OPTIND -le "$#" ]
do
    if getopts h:d:s: option
    then
        case $option
        in
            h) host_name="$OPTARG";;
            d) wait_time="$OPTARG";;
            s) script="$OPTARG";;
        esac
    else
        script_args+=("${!OPTIND}")
        ((OPTIND++))
    fi
done

"$script" "${script_args[@]}"
Run Code Online (Sandbox Code Playgroud)

测试.sh:

#!/bin/bash
echo "$0 $@"
Run Code Online (Sandbox Code Playgroud)

测试 OP 的案例:

$ PATH+=:.  # Use the cases as written without prepending ./ to the scripts
$ myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3
./test.sh param1 param2 param3
$ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh
./test.sh param1 param2 param3
$ myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3
./test.sh param1 param2 param3
Run Code Online (Sandbox Code Playgroud)

这是怎么回事:

getopts如果遇到位置参数将失败。如果将其用作循环条件,则只要位置参数出现在选项之前,循环就会过早中断,就像在两个测试用例中一样。

因此,只有在处理完所有参数后,此循环才会中断。如果getopts不识别某些东西,我们就假设它是一个位置参数,并在手动递增getopts计数器的同时将其填充到数组中。

可能的改进:

As written, the child script can't accept options (only positional parameters), since getopts in the wrapper script will eat those and print an error message, while treating any argument like a positional parameter:

$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./myshell.sh: illegal option -- a
./test.sh param1 param2 opt1 param3
Run Code Online (Sandbox Code Playgroud)

If we know the child script can only accept positional parameters, then myshell.sh should probably halt on an unrecognized option. That could be as simple as adding a default last case at the end of the case block:

            \?) exit 1;;
Run Code Online (Sandbox Code Playgroud)
$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./myshell.sh: illegal option -- a
Run Code Online (Sandbox Code Playgroud)

If the child script needs to accept options (as long as they don't collide with the options in myshell.sh), we could switch getopts to silent error reporting by prepending a colon to the option string:

    if getopts :h:d:s: option
Run Code Online (Sandbox Code Playgroud)

Then we'd use the default last case to stuff any unrecognized option into script_args:

            \?) script_args+=("-$OPTARG");;
Run Code Online (Sandbox Code Playgroud)
$ myshell.sh param1 param2 -h hostname -d waittime -s test.sh -a opt1 param3
./test.sh param1 param2 -a opt1 param3
Run Code Online (Sandbox Code Playgroud)

  • 这很好用并且回答了原始帖子。为什么点赞数这么少? (3认同)

小智 12

混合opts和args:

ARGS=""
echo "options :"
while [ $# -gt 0 ]
do
    unset OPTIND
    unset OPTARG
    while getopts as:c:  options
    do
    case $options in
            a)  echo "option a  no optarg"
                    ;;
            s)  serveur="$OPTARG"
                    echo "option s = $serveur"
                    ;;
            c)  cible="$OPTARG"
                    echo "option c = $cible"
                    ;;
        esac
   done
   shift $((OPTIND-1))
   ARGS="${ARGS} $1 "
   shift
done

echo "ARGS : $ARGS"
exit 1
Run Code Online (Sandbox Code Playgroud)

结果:

bash test.sh  -a  arg1 arg2 -s serveur -c cible  arg3
options :
option a  no optarg
option s = serveur
option c = cible
ARGS :  arg1  arg2  arg3
Run Code Online (Sandbox Code Playgroud)

  • 请使用构造`declare -a ARGS ..... ARGS+=($1)` 将值添加到列表中。这种方式尊重空间。 (3认同)
  • 对于ARGS变量,如果没有参数,这将导致一个更干净的字符串为空(即,没有空格):`ARGS =“ $ {ARGS:+ $ {ARGS}} $ {1}” (2认同)