【发布时间】: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