sourced Bash 函数中的 Getopts 交互工作,但在测试脚本中不行?

Getopts in sourced Bash function works interactively, but not in test script?

我有一个 Bash 函数 library 并且一个函数在测试中被证明是有问题的。 prunner 是一个函数,旨在提供 GNU Parallel 的一些功能,并避免尝试在 Perl 中使用其他 Bash 函数的范围问题。它支持针对 -c 的参数列表将命令设置为 运行,并同时将后台作业数设置为 运行 和 -t

在测试中,我遇到了以下情况:

测试是在 Ubuntu 16.04 和 Bash 4.3 以及 Mac OS X 和 Bash 4.4 上进行的。

后者在 test.bash 中发生的情况似乎是 getopts 拒绝处理 -c,因此 prunner 将尝试直接执行参数没有给出前缀命令。奇怪的是,我能够观察到它接受了 -t 选项,所以 getopts 至少部分起作用了。 Bash 使用 set -x 进行调试无法阐明为什么会发生这种情况。

这是有问题的函数,稍微修改为使用 echo 而不是 logquit 以便它可以与我的库的其余部分分开使用:

    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 以避免调用相互干扰。