将所有参数传递给另一个使用 getopts 的源 bash 脚本

将所有参数传递给另一个使用 getopts 的源 bash 脚本

我正在尝试将所有参数从一个脚本传递到另一个脚本。但是,当获取其他脚本时会出现问题;所有参数均未正确传递。

首先.sh:

#!/usr/bin/bash


while getopts a option 
do 
    case "${option}"
    in
    a) echo 'OptionA:somevalue';; 
    esac 
done

# This way works
./second.sh "$@"

# Does not work when source command is used
#source ./second.sh "$@"

第二个.sh:

#!/usr/bin/bash


while getopts b:c option 
do 
    case "${option}"
    in
    b) echo 'OptionB:'"${OPTARG}";; 
    c) echo 'OptionC:somevalue';;
    esac 
done

输出:

$ ./test.sh -a -b foo -c
OptionA:somevalue
./first.sh: illegal option -- b
./second.sh: illegal option -- a
OptionB:foo
OptionC:somevalue

预期输出:

$ ./test.sh -a -b foo -c
OptionA:somevalue
OptionB:foo
OptionC:somevalue

要实现什么目标?

使用source命令正确传递参数second.sh,摆脱illegal option并兼容其他shell。

编辑:用更清晰的示例再次更新了问题。

答案1

考虑while getopts循环在正常情况下如何工作。getopts为循环的每次迭代调用,即使它本身不修改命令行参数(位置参数),它也需要知道下一步要查看参数列表中的位置。它必须通过保持“隐藏”状态来做到这一点,这是在调用中未明确传递的东西。

这在Bash 的参考手册:

每次调用时,getopts将下一个选项放入 shell 变量中姓名, 初始化姓名如果不存在,以及要处理到变量 中的下一个参数的索引OPTINDOPTIND每次调用 shell 或 shell 脚本时都会初始化为 1。当选项需要参数时,getopts 将该参数放入变量 中OPTARG外壳不会OPTIND自动重置;getopts如果要使用一组新参数,则必须在同一 shell 调用中的多次调用之间手动重置它。

当您获取脚本时,它会在与主脚本相同的 shell 环境中运行,这包括OPTIND.

正如手册中所暗示的,只需OPTIND=1在开始新getopts循环之前设置即可。 (不要尝试取消设置,这可能会导致某些 shell 出现问题。)


举个例子:

set -- -a foo
while getopts a:b: option; do
    echo "$option: $OPTARG"
done
set -- -b first -b second
# OPTIND=1
while getopts a:b: option; do
    echo "$option: $OPTARG"
done

印刷

a: foo
b: second

因为第二个循环从第一个循环结束的位置继续,缺少-b first.

取消OPTIND=1中间分配的注释,您将获得预期的输出:

a: foo
b: first
b: second

至于与 分开存储参数$@args+="-a ${OPTARG} "会构建单个字符串,而命令行参数集实际上是不同字符串的列表/数组。当任何参数本身包含空格时,差异最为明显。两组参数 ( -a, foo bar) 和 ( -a foo, bar) 都连接成-a foo bar,并且无法区分后一个字符串。

相反,使用数组将参数存储为不同的字符串。

while getopts a:b:d option 
do 
    case "${option}"
    in
    a) args+=(-a "$OPTARG");; 
    b) args+=(-b "$OPTARG");; 
    d) echo 'Option:D';; 
    esac 
done
# ...
./args.sh "${args[@]}"

(看 我们如何运行存储在变量中的命令?有关更多示例等)


注意,标准.(点)命令源脚本不带参数,但源脚本$@与主脚本相同。 (源脚本中所做的更改$@在主脚本中可见。)

虽然 Bash 的./source支持传递一组新的参数,但如果您source filename在没有一组参数的情况下调用,则源脚本不会不是得到一个空列表,但有主脚本的参数。

所以,如果你这样做:

source ./args.sh "${args[@]}"

您可能需要注意检查它args不为空。或者只是在获取另一个脚本之前重置主脚本的参数。当然,这会破坏原始的参数集,但如果您需要它们,可以先将它们保存到另一个数组中:

orig_args=( "$@" )   # if needed 
set -- "${args[@]}"
source ./args.sh

相关内容