【问题标题】:Parsing --this=style command line arguments in bash在 bash 中解析 --this=style 命令行参数
【发布时间】:2020-02-04 00:26:54
【问题描述】:

我想在 bash 中解析 --this=style 命令行参数。

主要要求:

  • --abc=xyz 显然应该设置abc为xyz
  • --thing 应该设置 thing=true
  • --no-thing 应该设置thing=false
  • 定义默认值时噪音最小。

其他限制:

  • 以某种方式保留非 --this=style 参数很有帮助。
  • 我对短格式 -a -b -c 样式不感兴趣。
  • 要求某些参数的方法会很好。

我对可以内联到任何脚本的简短轻量级便携式解决方案以及提供更多功能的大型库样式解决方案都感兴趣。下面是我提出的一个中间解决方案(两全其美?),但我很想看看人们有什么其他想法来平衡长度和功能之间的权衡。

parse_args.sh

# Parse double dashed command line options.
#
# Parse positional options in the following formats:
#  --option         Sets {option} to "true".
#  --no-option      Sets {option} to "false".
#  --option=value   Sets {option} to {value}.
# 
# Option names must already be defined or an "unknown option" error will be
# printed to stderr.
#
# Output variables:
#   remaining_args  All remaining args in the order
#                   they were defined.
#
# Returns: 1 if there was an error.
parse_args() {
  remaining_args=() 
  for arg; do
    if [[ "$arg" =~ ^--(.+)$ ]]; then
      local k="${BASH_REMATCH[1]}"
      local v=true
      if [[ "$k" =~ ^([^=]+)=(.*)$ ]]; then
        k="${BASH_REMATCH[1]}"
        v="${BASH_REMATCH[2]}"
      elif [[ "$k" =~ ^no-(.*)$ ]]; then
        k="${BASH_REMATCH[1]}"
        v=false
      fi
      if [[ ! -v "$k" ]]; then
        echo "error: unknown option: '$k'" > /dev/stderr
        return 1
      fi
      eval $k="$v"
    else
      remaining_args+=("$arg")
    fi
  done 

  return 0
}

这是一个使用它的示例程序:

example.sh

#!/bin/bash

. parse_args.sh

# Declare defaults
breakfast="avocado toast"
lunch="burrito"
vegetarian=false
vegan=true

# Define help as a string, not a function, so that we have access to the
# defaults
help=$( cat - <<EOF
Usage: $0 --option=value other_food1 ... other_foodN

Options (with given defaults):

  breakfast ($breakfast)
    What to eat for breakfast.

  lunch ($lunch)
    What to eat for lunch.

  vegetarian ($vegetarian)
    Whether the meals should be vegetarian or not.

  vegan ($vegan)
    Whether the meals should be vegan or not.

other_foodN:
  Positional arguments are other foods.  These can be before, after, or in
  between options.
EOF
)

parse_args "$@" || { echo "$help" > /dev/stderr; exit 1 ; }
set -- "${remaining_args[@]}"

cat - <<EOF
  breakfast: '$breakfast'
      lunch: '$lunch'
 vegetarian: '$vegetarian'
      vegan: '$vegan'
other foods:
EOF

for p; do echo "  '$p'" ; done

运行它的样子:

$ ./example.sh \
  --breakfast=muesli mole \
  --lunch=rosti horchata \
  --vegetarian \
  --no-vegan

  breakfast: 'muesli'
      lunch: 'rosti'
 vegetarian: 'true'
      vegan: 'false'
other foods:
  'mole'
  'horchata'



编辑:

在深入挖掘时,我发现我的一位老同事最终从我的旧工作中移植了“重量级”库。 https://github.com/kward/shflags/blob/master/README.md 这正是我想要的“重量级”解决方案,感谢 Kate 移植它!不过,我仍然有兴趣看到人们对轻量级版本的想法。

【问题讨论】:

  • getopt 。通常在/usr/bin
  • 在发帖之前,我通读了How do I parse command line arguments in Bash?。那里的要求不同,但很多答案确实更接近我的方向,但没有一个是令人满意的。
  • 哦,关于 getopt 和 getopts,我不觉得它们是特别令人满意的选项,因为它们没有完成全部工作,它们仍然需要一些解析/循环,而且它最终变得相当不雅,带有相当多的噪音和重复。
  • 很棒的代码! Stackoverflow 不是满足对其他解决方案的好奇心的论坛。这是一个针对特定编程问题的论坛。 What topic can I ask about here?.. 你似乎已经解决了你的问题。 parse_args.sh 内部有很好的简单功能,你的问题不是更适合例如 codereview.stackexchange 而不是这里吗?
  • 哦,也许吧,虽然我正在寻找答案。例如,一个好的答案应该是这样的:github.com/kward/shflags/blob/master/README.md 看起来我的老同事实际上将图书馆从我的旧工作移植到了公共土地上,耶!谢谢凯特!这正是我在“重量级”解决方案中所希望的。不过,看看人们想出什么优雅的轻量级解决方案仍然会很有趣。

标签: bash


【解决方案1】:

我建议使用case

#!/bin/bash

while [[ $@ ]]; do
    case $1 in
        --abc=*   ) abc=${1/--abc=/};;
        --thing   ) thing=true ;;
        --no-thing) thing=false;;
        *         ) echo 'error'; exit 1;;
    esac
    shift
done

echo $abc $thing

以及使用这个的例子

$ ./test --abc='hello world!' --thing
hello world! true

【讨论】:

  • 所以我不喜欢案例解决方案的问题与 getopt 相同。它非常嘈杂,并且将解析与实际参数混合在一起。请注意,您必须在这里写 4 次“事物”,如果您想指定默认值,则需要第五次。
  • @BrianBraunstein 我相信您至少可以编写适当的正则表达式来合并这两行,但最终清晰度可能比保存一行更重要。
猜你喜欢
  • 2010-09-16
  • 1970-01-01
  • 2019-10-13
  • 2013-03-21
  • 2020-01-25
相关资源
最近更新 更多