如何在 shell 脚本中提取未知参数?

如何在 shell 脚本中提取未知参数?

我有一个 shell 脚本,它接受各种选项,有些带有参数,有些没有,有些短,有些长。它应该自己处理其中一些选项,并将其不知道如何关心的其余选项传递给另一个程序。我可以得到getopts类似的东西来存储未知的参数吗?

例如,假设我的脚本被调用program并且应该接受参数

  • -n用一个参数,
  • --verbose没有任何争论并且
  • -s没有任何争论。

它解析并打印所有选项及其参数,然后echo rest:使用剩余的任何内容进行调用。应观察以下输出。

> program -sin42 --long --verbose
-s
-n with argument 42
--verbose
rest: -i --long 

> program -n --short
-n with argument --short

> program -n
error: -n without argument

这样的事情可以在shell脚本中实现吗?

答案1

我不认为有一个可用的标准方法(即除了从头开始实现它之外),更不用说在 shell 中广泛使用了。

虽然ksh支持相当强大的getopts内置功能。基于此(以及您相当苛刻的要求),我ksh使用以下代码片段概述了一个可能的基础解决方案:

while getopts ":[-][[n]:][99:verbose][s]" opt
do  case $opt in
    (n) n_arg=$OPTARG ;;
    (99) verbose=1 ;;
    (s) s=1 ;;
    (*) arg_rest+=( "${@:OPTIND-1:1}" ) ;;
    esac
done
shift OPTIND-1

printf "main opt(%s)=%s\n" "-n" "$n_arg"
printf "main opt(%s)=%s\n" "--verbose" "$verbose"
printf "main opt(%s)=%s\n" "-s" "$s"

function delegate
{
    while getopts ":[-][i][98:long]" opt
    do  case $opt in
        (i) int=1 ;;
        (98) long=1 ;;
        esac
    done
    shift OPTIND-1

    printf "func opt(%s)=%s\n" "-i" "$int"
    printf "func opt(%s)=%s\n" "--long" "$long"
}

printf "Delegate: '%s'\n" "${arg_rest[@]}"
delegate "${arg_rest[@]}"

程序首先解析所有选项,根据需要设置内部变量,并将未知选项存储在数组中。然后你会看到一些printf控制设置的选项。然后是一个函数定义,其余选项将被委托给该函数;该函数也可以用命令代替。最后使用其余参数调用函数(或其他命令)。

ksh(有关功能的描述,getopts请从ksh会话中调用getopts --man。)

运行该程序会产生以下输出:

$ ksh ./getopts_script -s -n 23 --verbose -i --long
main opt(-n)=23
main opt(--verbose)=1
main opt(-s)=1
Delegate: '-i'
Delegate: '--long'
func opt(-i)=1
func opt(--long)=1



有关支持长选项的 getopts 函数的 shell 实现,请参阅https://github.com/stephane-chazelas/misc-scripts/blob/master/getopts_long.sh

答案2

人们很容易尝试getopt预先订购已知的选项。不幸的是,它拒绝所有未知参数并立即停止解析。

但是,只要长选项不是过于复杂,bashgetopts就可以被欺骗来执行此操作。一些灵感来自于mkaurball。来源下方附有注意事项。

#!/bin/bash

while [ $OPTIND -le $# ]
do
  if getopts ":sn:-:" argument
  then
    case $argument in
      s) echo "-$argument" ;;
      n) echo "-$argument with argument $OPTARG" ;;
      \?) pass+=("-$OPTARG") ;;
      -) lastarg=$((OPTIND - 1))
        case "${!lastarg}" in
          --verbose) echo "--verbose" ;;
          --*) pass+=("--$OPTARG") ;;
          *) echo "invalid argument: -"
            exit 1 ;;
        esac ;;
      :) echo "$OPTARG without argument"
        exit 1 ;;
    esac
  else
    pass+=("${!OPTIND}")
    let OPTIND++
  fi
done

echo pass: "${pass[@]}"
  • 不同寻常的是,getoptss 不用作 的条件while,这会导致循环在遇到任何位置参数(即既不是选项也不是参数的任何参数)时立即停止。相反,这些情况会附加到收集参数的数组中pass
  • 参数规范包括:-,这使得getopts将长选项视为带有参数的--long短选项。如果不小心使用,将破折号混合到短选项复合词(例如 )的中间可能会破坏这一点,因此最好检查实际参数是否确实以两个破折号开头。因此,如果包装的程序想要将选项视为选项,则该方法将会失败。--long-s-n-short-with-dashes
  • 尽管就 的本质而言getopts,双破折号也会被吞掉。这可能是想要的,但也可以通过在[ "${!OPTIND}" != '--' ] &&之前插入来避免getopts
  • 包装器解析长参数可能有些困难,因为它们必须在嵌套的case.

相关内容