源 Bash 函数中的 Getopts 以交互方式工作,但不在测试脚本中?

MrD*_*Coy 0 bash shell getopts

我有一个 Bash 函数,其中一个函数在测试时被证明存在问题。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

测试在使用 Bash 4.3 的 Ubuntu 16.04 和使用 Bash 4.4 的 Mac OS X 上进行。

这似乎与后者中发生的事情test.bash是,getopts拒绝处理-c,从而prunner将尝试直接执行论据没有它被赋予的前缀命令。奇怪的是,我能够观察到它接受该-t选项,因此getopts至少部分工作。Bash 调试set -x无法说明为什么我会发生这种情况。

这是有问题的函数,稍作修改以echo代替logand使用,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."
    }
Run Code Online (Sandbox Code Playgroud)

任何人都可以帮我确定为什么当线路通过管道连接到标准输入时-c选项不能正常工作prunner吗?

ric*_*ici 5

我的猜测是您正在同一个 shell 中执行这两个命令。在这种情况下,在第二次调用中,OPTIND将具有值 3(这是它在第一次调用时到达的位置)并且getopts将开始扫描。

如果您用于getopts解析函数的参数(而不是脚本),请声明local OPTIND=1以避免调用相互干扰。

  • 完全就是这样。不确定这些变量是如何设置的,因为我没有在测试脚本中的任何函数之外调用“getopts”。在调用“getopts”之前在函数中本地设置它们可以解决问题。谢谢! (2认同)
  • @MrDrMcCoy:您所要做的就是调用您的函数两次(或任何其他使用 getopts 的函数)。`OPTIND` 是一个 **global** 变量,包含所有暗示(除非声明为本地)。这是对 bash 变量作用域的好奇,您可以像这样将其声明为本地;bash 本地人在这方面更像 perl 本地人。 (2认同)