解析——bash中的this=style命令行参数

解析——bash中的this=style命令行参数,bash,Bash,我想在bash中解析--this=style命令行参数 主要要求: --abc=xyz显然应该将abc设置为xyz --事情应该是真的 --任何事物都不应该设置thing=false 定义默认值时,噪音最小 其他制约因素: 以某种方式保留非--this=样式的参数是有帮助的 我对缩写形式a-b-c不感兴趣 一种需要特定参数的方法会很好 我感兴趣的不仅是可以内联到任何脚本中的简短的轻量级便携解决方案,还包括提供更多功能的大型库式解决方案。下面是我设计的一个中间解决方案(两个世界中最糟糕的?

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

主要要求:

  • --abc=xyz显然应该将abc设置为xyz
  • --事情应该是真的
  • --任何事物都不应该设置thing=false
  • 定义默认值时,噪音最小
其他制约因素:

  • 以某种方式保留非--this=样式的参数是有帮助的
  • 我对缩写形式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
}
#!/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

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

示例。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
}
#!/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


编辑:

在进一步挖掘的过程中,我发现我的一位老同事最终从我的旧作品中移植了“重量级”图书馆。
这正是我想要的“重量级”解决方案,感谢Kate移植它!不过,我仍然有兴趣看到人们对轻量级版本的想法。

我建议使用
案例

#!/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
。它通常在
/usr/bin
中。发帖前我通读了一遍。那里的要求不同,但许多答案确实更接近我的方向,但没有一个是令人满意的。哦,关于getopt和getopts,我觉得它们不是特别令人满意的选项,因为它们没有完成全部工作,它们仍然需要一些解析/循环,它最终变得相当不公平,有相当多的噪音和重复。很棒的代码!Stackoverflow不是满足对其他解决方案好奇的论坛。这是一个讨论特定编程问题的论坛。。因为你似乎已经解决了你的问题。在
parse_args.sh
中有一个很好的简单函数,你的问题不是比这里更适合codereview.stackexchange吗?哦,也许吧,尽管我正在寻找答案。例如,一个好的答案应该是:看起来我的老同事实际上把图书馆从我的老作品搬到了公共土地上,耶!谢谢你,凯特!这正是我所希望的“重量级”解决方案。不过,看看人们为优雅的轻量级解决方案想出了什么样的解决方案还是很有趣的,所以我不喜欢案例解决方案,这与getopt的问题是一样的。它非常嘈杂,并且将解析和实际参数混合在一起。请注意,您必须在此处写入“thing”4次,如果要指定默认值,则需要第五次。@BrianBraunstein我相信您至少可以编写适当的正则表达式来合并这两行,但最终,清晰度可能比保存一行更重要。