【问题标题】:Cross-platform getopt for a shell script用于 shell 脚本的跨平台 getopt
【发布时间】:2010-04-27 14:19:09
【问题描述】:

我刚刚发现getopt 不是跨平台的(尤其是对于 FreeBSD 和 Linux)。解决此问题的最佳方法是什么?

【问题讨论】:

  • 如果您开始使用带有长选项等的 GNU getopt,并且希望通过尽可能少的更改使您现有的代码可移植,那么您可能会发现 stackoverflow.com/a/37087374/324105 很有用。

标签: bash shell cross-platform getopt


【解决方案1】:

getopt 命令基本上有两个版本:原始版本和 GNU 增强版本。 GNU 增强版向后兼容原始版本,因此如果您只使用原始版本的功能,则两者都可以使用。

检测可用的 getopt 版本

如果 GNU 增强版本可用,您可以检测哪个版本可用并使用增强功能,如果 GNU 增强版本不可用,您可以限制自己使用原始功能。增强版有一个-T 选项,用于测试哪个版本可用。

getopt -T > /dev/null
if [ $? -eq 4 ]; then
    # GNU enhanced getopt is available
    set -- `getopt --long help,output:,version --options ho:v -- "$@"`
else
    # Original getopt is available
    set -- `getopt ho:v "$@"`
fi

考虑使用内置的 shell 命令getopts(带有“s”),因为它更便携。但是,getopts 不支持长选项(例如 --help)。

如果您喜欢长选项,请使用getopt 并使用上述测试查看getopt 的GNU 增强版本是否可用。如果增强版本不可用,脚本可以优雅地降级为使用 getopt 的原始版本(不支持长选项名称和不支持空格)或使用 getopts(不支持长选项名称) .

正确使用 GNU 增强的 getopt

让 GNU 增强版本正确处理带有空格的参数是很棘手的。以下是它的完成方式:

ARGS=`getopt --long help,output:,verbose --options ho:v -- "$@"`
if [ $? -ne 0 ]; then
  echo "Usage error (use -h for help)" >&2
  exit 2
fi
eval set -- $ARGS

# Parameters are now sorted: options appear first, followed by --, then arguments
# e.g. entering: "foo bar" -o abc baz -v
#      produces: -o 'abc' -v -- 'foo bar' 'baz'

秘诀是在双引号非常重要的地方使用"$@"(在第1行),在eval使用set命令(在第6行)。

因此可以检测和处理getopt 引发的错误,对getopt 的调用与eval 分开进行,两者通过ARGS 变量链接。

完整的工作示例

PROG=`basename $0`

getopt -T > /dev/null
if [ $? -eq 4 ]; then
  # GNU enhanced getopt is available
  ARGS=`getopt --name "$PROG" --long help,output:,verbose --options ho:v -- "$@"`
else
  # Original getopt is available (no long option names, no whitespace, no sorting)
  ARGS=`getopt ho:v "$@"`
fi
if [ $? -ne 0 ]; then
  echo "$PROG: usage error (use -h for help)" >&2
  exit 2
fi
eval set -- $ARGS

while [ $# -gt 0 ]; do
    case "$1" in
        -h | --help)     HELP=yes;;
        -o | --output)   OUTFILE="$2"; shift;;
        -v | --verbose)  VERBOSE=yes;;
        --)              shift; break;; # end of options
    esac
    shift
done

if [ $# -gt 0 ]; then
  # Remaining parameters can be processed
  for ARG in "$@"; do
    echo "$PROG: argument: $ARG"
  done
fi

echo "$PROG: verbose: $VERBOSE"
echo "$PROG: output: $OUTFILE"
echo "$PROG: help: $HELP"

这个例子可以从https://gist.github.com/hoylen/6607180下载

Wikipedia's entry on getopts 上的比较表比较了不同的功能。

【讨论】:

  • 是的,getopts(带有 s)是另一种选择。便携性纯粹主义者仍然更喜欢 getopt,因为在 1986 年之前,getopts 在古代 Bourne shell 中不可用,但这是一个糟糕的理由,因为大多数/所有现代 shell 都支持 get opts。一个更好的理由是,如果 GNU 增强的 getup 可用的话,可以很容易地利用它。 GNU 增强的 getopt 允许操作数与选项混合并支持长选项名称(getopts 不支持这两个功能 - 请参阅 [比较表][1])。
  • 为什么当我运行eval set -- $ARGS时它打印一个空的--?这很烦人。
  • 在我四处挖掘之后,事实证明这是目前我可以通过一点修改获得的最佳解决方案!如果 getopt 不是 GNU 增强的,我使用 getopts,以便可以更正确地处理空格并与增强的空格兼容。
【解决方案2】:

使用getopts(带有“s”)。

根据Bash FAQ 35

除非它是来自 util-linux 的版本,并且您使用它的高级模式,否则永远不要使用 getopt(1)。 getopt 无法处理空参数字符串或带有嵌入空格的参数。请忘记它曾经存在过。

POSIX shell(和其他)提供 getopts,可以安全使用。

【讨论】:

  • 我用助记符“S”表示Shell,getopt“S”内置在BASH中
  • @Dennis Williamson 请更新您的答案:根据(更新的)常见问题解答条目:除非它是 util-linux 的版本,并且您使用其高级模式,否则永远不要使用 getopt(1) . ...
  • @Florian:已更新。谢谢。
【解决方案3】:

getopt 的基本语法是跨平台的。

getopt vi: -v -i 100 file

【讨论】:

    【解决方案4】:

    Bash 内置的 getopts 函数可用于便携式解析短选项和长选项,请参阅:

    Using getopts in bash shell script to get long and short command line options

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-28
      • 1970-01-01
      • 2017-06-06
      • 2014-01-18
      • 1970-01-01
      • 2016-01-03
      • 1970-01-01
      • 2011-10-21
      相关资源
      最近更新 更多