在 bash 中多次调用 getopts
Multiple calls to getopts in bash
当这个脚本被调用时
test.sh -a 1 -b 2 -c 3
输出是:
-a 1 -b 2
1
我期待 parseRemainingArgs
解析并打印所有参数。
如您所见,所有参数都已正确传递给 parseRemainingArgs
,如第一行输出所示,但它仅解析第一个参数。
我也设置了 OPTIND,但这也没有帮助。
需要帮助来确定问题所在以及如何解决。
尝试了 google 提供的所有可用答案,但其中 none 解决了这个问题。
#!/bin/sh
parseRemainingArgs()
{
echo $@
OPTIND=1
while getopts :ha:b:c: o;
do
case "$o" in
h) echo "Print help";;
a) FIRST_ARG="$OPTARG";;
b) SECOND_ARG="$OPTARG";;
c) THIRD_ARG="$OPTARG";;
\?) ;;
esac
done
echo $FIRST_ARG $SECOND_ARG $THIRD_ARG
}
parseArgs()
{
AllArgs=$@
while getopts :ht: o;
do
case "$o" in
t) TARGET="$OPTARG";;
h) HELP="yes";;
\?) ;;
esac
done
shift $(($OPTIND - 1))
parseRemainingArgs $AllArgs
}
parseArgs $@
修改后的示例命令行
Chhaba :
To get the problem can you please test your script with the following arguments
gomod.sh -t a -a 1 -b 2 -c 3
In this case -t
will match in parseArgs
but parseRemainingArgs
will fail to parse the remaining args. It works great when -t
or -h
is not given which matches in parseArgs
.
好的;这就是我对我使用的脚本的期望(请参阅下面“'Cannot reproduce' 响应”部分中的脚本——它是对问题内容的温和扩展)。从 bash
,我得到:
$ bash gomod.sh -t a -a 1 -b 2 -c 3
Initial arguments: -t a -a 1 -b 2 -c 3
Doing nothing
OPTIND = 4
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -t a -a 1 -b 2 -c 3
Ignoring error
a1: a2: a3
$
这是您在 parseArgs
开头设置 AllArgs
的结果。当您在 parseArgs
中解析 "$@"
中的参数时,它不会改变,因此您将原始参数列表传递给 parseRemainingArgs
,如诊断输出所示。在 parseRemainingArgs
中,-t
不是一个选项,因此循环 returns 在 -t
上出错(因此 Ignoring error
),然后退出,因为 a
不是一个选项。
您可以从以下位置获得更多输出:
$ bash gomod.sh -t -a 1 -b 2 -c 3
Initial arguments: -t -a 1 -b 2 -c 3
OPTIND = 3
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -t -a 1 -b 2 -c 3
Ignoring error
a1: 1 a2: 2 a3 3
$
它忽略 parseRemainingArgs
中的 -t
错误,然后使用相应的参数处理每个 -a
、-b
和 -c
。
或者,您可以修改脚本,将 parseRemainingArgs
的调用替换为:
parseRemainingArgs "$@"
然后您可以 运行 像这样,例如,得到显示的输出:
$ bash gomod.sh -t antelope -- -a 1 -b 22 -c 333
Initial arguments: -t antelope -- -a 1 -b 22 -c 333
OPTIND = 4
Residual arguments: -a 1 -b 22 -c 333
Parsing Remaining: -a 1 -b 22 -c 333
a1: 1 a2: 22 a3 333
$
请注意,将 --
替换为空或替换为 abelone
(或任何其他非选项)不会如您所愿。
您仍然可以添加更多调试以使其更易于理解,或者 运行 在 bash -x
.
下
'Cannot reproduce' 回应
我不确定你的问题是什么。我已经 运行 对您的脚本进行了稍微修改的版本——更多的诊断和使用反引号和 expr
来计算偏移(因为我测试的一些 shells 不支持 $((…))
算术符号)。代码是:
#!/bin/sh
parseRemainingArgs()
{
echo "Parsing Remaining:" "$@"
OPTIND=1
while getopts :ha:b:c: o
do
case "$o" in
h) echo "Print help";;
a) FIRST_ARG="$OPTARG";;
b) SECOND_ARG="$OPTARG";;
c) THIRD_ARG="$OPTARG";;
\?) echo "Ignoring error";;
esac
done
echo a1: $FIRST_ARG a2: $SECOND_ARG a3 $THIRD_ARG
}
parseArgs()
{
AllArgs=$@
echo "Initial arguments:" "$@"
while getopts :ht: o
do
case "$o" in
t) TARGET="$OPTARG";;
h) HELP="yes";;
\?) echo "Doing nothing";;
esac
done
#shift $(($OPTIND - 1))
echo "OPTIND = $OPTIND"
shift `expr $OPTIND - 1`
echo "Residual arguments:" "$@"
parseRemainingArgs $AllArgs
}
parseArgs "$@"
测试的 shell 是:
sh
— link 到 bash
bash
dash
ksh
— 科恩 shell
zsh
hsh
— 传家宝 shell(靠近 Bourne shell)
svr4-sh
— Unix System V Release 4(略微加强了 Bourne shell)
其中,hsh
和 svr4-sh
没有接受 shift $(($OPTIND - 1))
。我有点惊讶传家宝 shell 竟然能认出 getopts
。
脚本被调用了gomod.sh
,我得到的输出是:
$ for shell in sh bash dash ksh zsh hsh svr4-sh
> do
> boxecho $shell
> $shell gomod.sh -a 1 -b 2 -c 3
> echo
> done
********
** sh **
********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
**********
** bash **
**********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
**********
** dash **
**********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*********
** ksh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*********
** zsh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 -b 2 -c 3 a2: a3
*********
** hsh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*************
** svr4-sh **
*************
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
$
boxecho
是一个简单的脚本,它在星号框内呼应其参数:
echo "** $@ **" | sed -e h -e 's/./*/g' -e p -e x -e p -e x
自 1998 年以来我就没有修改过它;我不经常有一个子目录 s
,里面的子目录包含一个文件 g
,所以 s/./*/g
周围没有引号从来没有让我失望,但我已经修复了上面的脚本(在我的 bin
目录中)。还有其他写 sed
的方法;然而,这确实有效。
值得注意的是,在 parseRemainingArgs
函数中使用 getopts
与最初传递给命令的全套参数一起工作,尽管暗示它正在处理 'the remaining arguments'.如果要处理尚未处理的参数,您会将 "$@"
传递给函数。它也会立即 运行 进入 'end of arguments' 除非你有一个 GNU getopts
功能而没有在环境中设置 POSIXLY_CORRECT 因为第一个这样的参数是 1
这是不是一个选项,会终止 getopts
处理。
当这个脚本被调用时
test.sh -a 1 -b 2 -c 3
输出是:
-a 1 -b 2
1
我期待 parseRemainingArgs
解析并打印所有参数。
如您所见,所有参数都已正确传递给 parseRemainingArgs
,如第一行输出所示,但它仅解析第一个参数。
我也设置了 OPTIND,但这也没有帮助。
需要帮助来确定问题所在以及如何解决。
尝试了 google 提供的所有可用答案,但其中 none 解决了这个问题。
#!/bin/sh
parseRemainingArgs()
{
echo $@
OPTIND=1
while getopts :ha:b:c: o;
do
case "$o" in
h) echo "Print help";;
a) FIRST_ARG="$OPTARG";;
b) SECOND_ARG="$OPTARG";;
c) THIRD_ARG="$OPTARG";;
\?) ;;
esac
done
echo $FIRST_ARG $SECOND_ARG $THIRD_ARG
}
parseArgs()
{
AllArgs=$@
while getopts :ht: o;
do
case "$o" in
t) TARGET="$OPTARG";;
h) HELP="yes";;
\?) ;;
esac
done
shift $(($OPTIND - 1))
parseRemainingArgs $AllArgs
}
parseArgs $@
修改后的示例命令行
Chhaba
To get the problem can you please test your script with the following arguments
gomod.sh -t a -a 1 -b 2 -c 3
In this case
-t
will match inparseArgs
butparseRemainingArgs
will fail to parse the remaining args. It works great when-t
or-h
is not given which matches inparseArgs
.
好的;这就是我对我使用的脚本的期望(请参阅下面“'Cannot reproduce' 响应”部分中的脚本——它是对问题内容的温和扩展)。从 bash
,我得到:
$ bash gomod.sh -t a -a 1 -b 2 -c 3
Initial arguments: -t a -a 1 -b 2 -c 3
Doing nothing
OPTIND = 4
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -t a -a 1 -b 2 -c 3
Ignoring error
a1: a2: a3
$
这是您在 parseArgs
开头设置 AllArgs
的结果。当您在 parseArgs
中解析 "$@"
中的参数时,它不会改变,因此您将原始参数列表传递给 parseRemainingArgs
,如诊断输出所示。在 parseRemainingArgs
中,-t
不是一个选项,因此循环 returns 在 -t
上出错(因此 Ignoring error
),然后退出,因为 a
不是一个选项。
您可以从以下位置获得更多输出:
$ bash gomod.sh -t -a 1 -b 2 -c 3
Initial arguments: -t -a 1 -b 2 -c 3
OPTIND = 3
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -t -a 1 -b 2 -c 3
Ignoring error
a1: 1 a2: 2 a3 3
$
它忽略 parseRemainingArgs
中的 -t
错误,然后使用相应的参数处理每个 -a
、-b
和 -c
。
或者,您可以修改脚本,将 parseRemainingArgs
的调用替换为:
parseRemainingArgs "$@"
然后您可以 运行 像这样,例如,得到显示的输出:
$ bash gomod.sh -t antelope -- -a 1 -b 22 -c 333
Initial arguments: -t antelope -- -a 1 -b 22 -c 333
OPTIND = 4
Residual arguments: -a 1 -b 22 -c 333
Parsing Remaining: -a 1 -b 22 -c 333
a1: 1 a2: 22 a3 333
$
请注意,将 --
替换为空或替换为 abelone
(或任何其他非选项)不会如您所愿。
您仍然可以添加更多调试以使其更易于理解,或者 运行 在 bash -x
.
'Cannot reproduce' 回应
我不确定你的问题是什么。我已经 运行 对您的脚本进行了稍微修改的版本——更多的诊断和使用反引号和 expr
来计算偏移(因为我测试的一些 shells 不支持 $((…))
算术符号)。代码是:
#!/bin/sh
parseRemainingArgs()
{
echo "Parsing Remaining:" "$@"
OPTIND=1
while getopts :ha:b:c: o
do
case "$o" in
h) echo "Print help";;
a) FIRST_ARG="$OPTARG";;
b) SECOND_ARG="$OPTARG";;
c) THIRD_ARG="$OPTARG";;
\?) echo "Ignoring error";;
esac
done
echo a1: $FIRST_ARG a2: $SECOND_ARG a3 $THIRD_ARG
}
parseArgs()
{
AllArgs=$@
echo "Initial arguments:" "$@"
while getopts :ht: o
do
case "$o" in
t) TARGET="$OPTARG";;
h) HELP="yes";;
\?) echo "Doing nothing";;
esac
done
#shift $(($OPTIND - 1))
echo "OPTIND = $OPTIND"
shift `expr $OPTIND - 1`
echo "Residual arguments:" "$@"
parseRemainingArgs $AllArgs
}
parseArgs "$@"
测试的 shell 是:
sh
— link 到bash
bash
dash
ksh
— 科恩 shellzsh
hsh
— 传家宝 shell(靠近 Bourne shell)svr4-sh
— Unix System V Release 4(略微加强了 Bourne shell)
其中,hsh
和 svr4-sh
没有接受 shift $(($OPTIND - 1))
。我有点惊讶传家宝 shell 竟然能认出 getopts
。
脚本被调用了gomod.sh
,我得到的输出是:
$ for shell in sh bash dash ksh zsh hsh svr4-sh
> do
> boxecho $shell
> $shell gomod.sh -a 1 -b 2 -c 3
> echo
> done
********
** sh **
********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
**********
** bash **
**********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
**********
** dash **
**********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*********
** ksh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*********
** zsh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 -b 2 -c 3 a2: a3
*********
** hsh **
*********
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
*************
** svr4-sh **
*************
Initial arguments: -a 1 -b 2 -c 3
Doing nothing
OPTIND = 2
Residual arguments: 1 -b 2 -c 3
Parsing Remaining: -a 1 -b 2 -c 3
a1: 1 a2: 2 a3 3
$
boxecho
是一个简单的脚本,它在星号框内呼应其参数:
echo "** $@ **" | sed -e h -e 's/./*/g' -e p -e x -e p -e x
自 1998 年以来我就没有修改过它;我不经常有一个子目录 s
,里面的子目录包含一个文件 g
,所以 s/./*/g
周围没有引号从来没有让我失望,但我已经修复了上面的脚本(在我的 bin
目录中)。还有其他写 sed
的方法;然而,这确实有效。
值得注意的是,在 parseRemainingArgs
函数中使用 getopts
与最初传递给命令的全套参数一起工作,尽管暗示它正在处理 'the remaining arguments'.如果要处理尚未处理的参数,您会将 "$@"
传递给函数。它也会立即 运行 进入 'end of arguments' 除非你有一个 GNU getopts
功能而没有在环境中设置 POSIXLY_CORRECT 因为第一个这样的参数是 1
这是不是一个选项,会终止 getopts
处理。