sourced Bash 函数中的 Getopts 交互工作,但在测试脚本中不行?
Getopts in sourced Bash function works interactively, but not in test script?
我有一个 Bash 函数 library 并且一个函数在测试中被证明是有问题的。 prunner
是一个函数,旨在提供 GNU Parallel 的一些功能,并避免尝试在 Perl 中使用其他 Bash 函数的范围问题。它支持针对 -c
的参数列表将命令设置为 运行,并同时将后台作业数设置为 运行 和 -t
。
在测试中,我遇到了以下情况:
prunner -c "gzip -fk" *.out
- 在 test.bash
中以交互方式按预期工作。
find . -maxdepth 1 -name "*.out" | prunner -c echo -t 6
- 不起作用,似乎忽略了 -c echo
.
测试是在 Ubuntu 16.04 和 Bash 4.3 以及 Mac OS X 和 Bash 4.4 上进行的。
后者在 test.bash
中发生的情况似乎是 getopts
拒绝处理 -c
,因此 prunner
将尝试直接执行参数没有给出前缀命令。奇怪的是,我能够观察到它接受了 -t
选项,所以 getopts
至少部分起作用了。 Bash 使用 set -x
进行调试无法阐明为什么会发生这种情况。
这是有问题的函数,稍微修改为使用 echo
而不是 log
和 quit
以便它可以与我的库的其余部分分开使用:
prunner () {
local PQUEUE=()
while getopts ":c:t:" OPT ; do
case ${OPT} in
c) local PCMD="${OPTARG}" ;;
t) local THREADS="${OPTARG}" ;;
:) echo "ERROR: Option '-${OPTARG}' requires an argument." ;;
*) echo "ERROR: Option '-${OPTARG}' is not defined." ;;
esac
done
shift $(($OPTIND-1))
for ARG in "$@" ; do
PQUEUE+=("$ARG")
done
if [ ! -t 0 ] ; then
while read -r LINE ; do
PQUEUE+=("$LINE")
done
fi
local QCOUNT="${#PQUEUE[@]}"
local INDEX=0
echo "Starting parallel execution of $QCOUNT jobs with ${THREADS:-8} threads using command prefix '$PCMD'."
until [ ${#PQUEUE[@]} == 0 ] ; do
if [ "$(jobs -rp | wc -l)" -lt "${THREADS:-8}" ] ; then
echo "Starting command in parallel ($(($INDEX+1))/$QCOUNT): ${PCMD} ${PQUEUE[$INDEX]}"
eval "${PCMD} ${PQUEUE[$INDEX]}" || true &
unset PQUEUE[$INDEX]
((INDEX++)) || true
fi
done
wait
echo "Parallel execution finished for $QCOUNT jobs."
}
任何人都可以帮我确定为什么 -c
选项在将行通过管道传输到标准输入时无法正常工作 prunner
吗?
也许您已经这样做了,但请确保将 top-level shell 参数传递给您的函数。该函数将通过调用接收参数,例如:
xyz () {
echo "First arg: "
echo "Second arg: "
}
xyz "This is" "very simple"
在您的示例中,您应该始终使用标准参数调用函数,以便它们可以在方法中通过 getopts 进行处理。
prunner "$@"
请注意,pruner 不会修改函数外的标准参数。
我的猜测是您在同一个 shell 中执行这两个命令。在这种情况下,在第二次调用中,OPTIND
将具有值 3(这是它在第一次调用时到达的位置),这就是 getopts
将开始扫描的位置。
如果您使用 getopts
来解析函数的参数(而不是脚本),请声明 local OPTIND=1
以避免调用相互干扰。
我有一个 Bash 函数 library 并且一个函数在测试中被证明是有问题的。 prunner
是一个函数,旨在提供 GNU Parallel 的一些功能,并避免尝试在 Perl 中使用其他 Bash 函数的范围问题。它支持针对 -c
的参数列表将命令设置为 运行,并同时将后台作业数设置为 运行 和 -t
。
在测试中,我遇到了以下情况:
prunner -c "gzip -fk" *.out
- 在test.bash
中以交互方式按预期工作。find . -maxdepth 1 -name "*.out" | prunner -c echo -t 6
- 不起作用,似乎忽略了-c echo
.
测试是在 Ubuntu 16.04 和 Bash 4.3 以及 Mac OS X 和 Bash 4.4 上进行的。
后者在 test.bash
中发生的情况似乎是 getopts
拒绝处理 -c
,因此 prunner
将尝试直接执行参数没有给出前缀命令。奇怪的是,我能够观察到它接受了 -t
选项,所以 getopts
至少部分起作用了。 Bash 使用 set -x
进行调试无法阐明为什么会发生这种情况。
这是有问题的函数,稍微修改为使用 echo
而不是 log
和 quit
以便它可以与我的库的其余部分分开使用:
prunner () {
local PQUEUE=()
while getopts ":c:t:" OPT ; do
case ${OPT} in
c) local PCMD="${OPTARG}" ;;
t) local THREADS="${OPTARG}" ;;
:) echo "ERROR: Option '-${OPTARG}' requires an argument." ;;
*) echo "ERROR: Option '-${OPTARG}' is not defined." ;;
esac
done
shift $(($OPTIND-1))
for ARG in "$@" ; do
PQUEUE+=("$ARG")
done
if [ ! -t 0 ] ; then
while read -r LINE ; do
PQUEUE+=("$LINE")
done
fi
local QCOUNT="${#PQUEUE[@]}"
local INDEX=0
echo "Starting parallel execution of $QCOUNT jobs with ${THREADS:-8} threads using command prefix '$PCMD'."
until [ ${#PQUEUE[@]} == 0 ] ; do
if [ "$(jobs -rp | wc -l)" -lt "${THREADS:-8}" ] ; then
echo "Starting command in parallel ($(($INDEX+1))/$QCOUNT): ${PCMD} ${PQUEUE[$INDEX]}"
eval "${PCMD} ${PQUEUE[$INDEX]}" || true &
unset PQUEUE[$INDEX]
((INDEX++)) || true
fi
done
wait
echo "Parallel execution finished for $QCOUNT jobs."
}
任何人都可以帮我确定为什么 -c
选项在将行通过管道传输到标准输入时无法正常工作 prunner
吗?
也许您已经这样做了,但请确保将 top-level shell 参数传递给您的函数。该函数将通过调用接收参数,例如:
xyz () {
echo "First arg: "
echo "Second arg: "
}
xyz "This is" "very simple"
在您的示例中,您应该始终使用标准参数调用函数,以便它们可以在方法中通过 getopts 进行处理。
prunner "$@"
请注意,pruner 不会修改函数外的标准参数。
我的猜测是您在同一个 shell 中执行这两个命令。在这种情况下,在第二次调用中,OPTIND
将具有值 3(这是它在第一次调用时到达的位置),这就是 getopts
将开始扫描的位置。
如果您使用 getopts
来解析函数的参数(而不是脚本),请声明 local OPTIND=1
以避免调用相互干扰。