61

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

if echo $* | grep -e "--flag" -q
then
  echo ">>>> Running with flag"
else
  echo ">>>> Running without flag"
fi

有没有更好的办法?

注意:我明确不想列出 switch/getopt 中的所有标志。(在这种情况下,任何这样的事情都将成为完整脚本的一半或更多。if 的主体也只是设置了一组变量)

4

8 回答 8

79

您正在做的事情的替代方法:

if [[ $* == *--flag* ]]

另见BashFAQ/035

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

于 2010-05-20T17:16:18.773 回答
15

我通常看到这是通过一个案例陈述完成的。这是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

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

于 2010-05-20T15:53:41.500 回答
11

您可以采用直截了当的方法,并遍历参数以测试每个参数与给定参数(例如-t--therizinosaurus)的相等性。

把它放到一个函数中:

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

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

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

if ! has_param '-t' "$1" "$2" "$wat"; then
    echo "nay..."
fi

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

for arg; do
    if [[ -z "$arg" ]]; then
        return 2
    fi
    # ...

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


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

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

我倾向于重用,但我必须意识到其中的含义。相反的方法是在脚本顶部解析这些参数一次,就像你害怕的那样,并避免重复解析。
您仍然可以封装该开关盒,它可以根据您的决定而定(您不必列出所有选项)。

于 2019-06-03T16:24:23.393 回答
7

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

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

获取选择

这是一个独立的可执行文件,已经存在了很长时间。旧版本缺乏处理引用参数的能力(foo a“这不起作用”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
于 2010-05-20T15:50:33.097 回答
3

我对Eliran Malka的回答做了一些小改动:

此函数可以评估不同的参数同义词,例如“-q”和“--quick”。此外,它不使用 return 0/1 而是使用 echo 在找到参数时返回非空值:

function has_param() {
    local terms="$1"
    shift

    for term in $terms; do
        for arg; do
            if [[ $arg == "$term" ]]; then
                echo "yes"
            fi
        done
    done
}

# Same usage:

# Assign result to a variable.
FLAG_QUICK=$(has_param "-q --quick" "$@")  # "yes" or ""

# Test in a condition using the nonzero-length-test to detect "yes" response.
if [[ -n $(has_param "-h --help" "$@") ]]; then; 
    echo "Need help?"
fi

# Check, is a flag is NOT set by using the zero-length test.
if [[ -z $(has_param "-f --flag" "$@") ]]; then
    echo "FLAG NOT SET"
fi
于 2020-09-16T12:00:19.057 回答
3
if [ "$1" == "-n" ]; then
    echo "Flag set";
fi
于 2021-02-24T20:32:42.820 回答
2

修改丹尼斯威廉姆森答案,并附有简短形式的论证的附加示例。

if [[ \ $*\  == *\ --flag\ * ]] || [[ \ $*\  == *\ -f\ * ]]

它解决了误报匹配的问题,--flags-off甚至解决了--another--flag(对于单虚线参数更流行的这种情况:--one-more-flagfor *-f*)。

\ (反斜杠 + 空格) 表示[[ ]]. 在周围放置空格$*可以确保参数既不接触行的开头也不接触行的结尾,它们只接触空格。现在可以在带有参数的行中搜索被空格包围的目标标志。

于 2021-01-20T14:50:30.260 回答
1

不过,这不是替代方案,而是改进。

if echo $* | grep -e "\b--flag\b" -q

寻找单词边界将确保真正获得选择--flag,既不--flagstaff也不--not-really--flag

于 2020-11-19T14:02:13.147 回答