在bash中检查命令行标志的正确方法

BCS*_*BCS 40 bash flags command-line

在脚本中间,我想检查命令行上是否传递了给定的标志.以下是我想要的,但看起来很难看:

if echo $* | grep -e "--flag" -q
then
  echo ">>>> Running with flag"
else
  echo ">>>> Running without flag"
fi
Run Code Online (Sandbox Code Playgroud)

有没有更好的办法?

注:我明确地希望列出一个开关/ getopt的所有标志.(在这种情况下,任何这样的东西都会变成完整脚本的一半或更多.而且if的主体也只是设置了一组变量)

Pau*_*ce. 60

替代你正在做的事情:

if [[ $* == *--flag* ]]
Run Code Online (Sandbox Code Playgroud)

另见BashFAQ/035.

注意:这也将匹配,--flags-off因为它是一个简单的子字符串检查.

  • 那不是也会误报吗?例如,如果您选择`* -f *`,它将匹配`--force`,`-fish`,`--update-files`,`-wtf`等 (3认同)
  • @nathanchere:仔细选择匹配字符串很重要。请注意,您的示例允许您列出的所有匹配项,但我的示例留下的误报可能性要小得多。鉴于 OP 对简单解决方案的要求,我的解决方案非常适合。否则使用 getopt 或 getopts。 (2认同)
  • 这正是我的观点。我的例子是故意简化的,但仍然表现出与你的相同的问题。例如,如果您使用“*--flag*”,您将匹配“--flag”、“--flags-off”等。即,这是一个草率的解决方案 (2认同)
  • @nathanchere 欢迎您发布满足 OP 要求的更好解决方案。 (2认同)

Eli*_*lka 11

您可以采用简单的方法,并迭代参数以测试每个参数与给定参数(例如-t)的相等性。

把它放到一个函数中:

has_param() {
    local term="$1"
    shift
    for arg; do
        if [[ $arg == "$term" ]]; then
            return 0
        fi
    done
    return 1
}
Run Code Online (Sandbox Code Playgroud)

...并将其用作测试表达式中的谓词:

if has_param '-t' "$@"; then
    echo "yay!"
fi

if ! has_param '-t' "$1" "$2" "$wat"; then
    echo "nay..."
fi
Run Code Online (Sandbox Code Playgroud)

如果你想拒绝空参数,在循环体的顶部添加一个退出点:

for arg; do
    if [[ -z "$arg" ]]; then
        return 2
    fi
    # ...
Run Code Online (Sandbox Code Playgroud)

这是非常易读的,不会给你误报,比如模式匹配或正则表达式匹配。
它还允许在任意位置放置标志,例如,您可以放置-h在命令行的末尾(不考虑它的好坏)。


但是,我想得越多,就越困扰我。

使用函数,您可以采用任何实现(例如getopts),并重用它。封装规则!
但即使有命令,这种力量也会成为缺陷。如果你一次又一次地使用它,你每次都会解析所有的参数。

我倾向于重用,但我必须意识到其中的含义。相反的方法是在脚本顶部解析这些参数一次,正如您所担心的那样,并避免重复解析。
您仍然可以封装那个 switch case,它可以像您决定的那样大(您不必列出所有选项)。

  • 这是最好的答案! (2认同)

Kal*_*son 9

我通常会在案例陈述中看到这一点.以下是git-repack脚本的摘录:

while test $# != 0
do
    case "$1" in
    -n) no_update_info=t ;;
    -a) all_into_one=t ;;
    -A) all_into_one=t
        unpack_unreachable=--unpack-unreachable ;;
    -d) remove_redundant=t ;;
    -q) GIT_QUIET=t ;;
    -f) no_reuse=--no-reuse-object ;;
    -l) local=--local ;;
    --max-pack-size|--window|--window-memory|--depth)
        extra="$extra $1=$2"; shift ;;
    --) shift; break;;
    *)  usage ;;
    esac
    shift
done
Run Code Online (Sandbox Code Playgroud)

请注意,这允许您检查短标志和长标志.extra在这种情况下,使用变量构建其他选项.


Whi*_*ind 6

您可以在bash中使用getopt关键字.

来自http://aplawrence.com/Unix/getopts.html:

getopt的

这是一个独立的可执行文件,已经存在很长时间了.较旧的版本缺乏处理引用参数的能力(foo"这将无效"c)和版本可以,笨拙地这样做.如果您运行的是最近的Linux版本,那么"getopt"可以做到这一点; SCO OSR5,Mac OS X 10.2.6和FreeBSD 4.4的旧版本没有.

这个迷你脚本中显示了"getopt"的简单使用:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done
Run Code Online (Sandbox Code Playgroud)