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

问题描述 投票:0回答:11

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

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

还有更好的方法吗?

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

bash command-line flags
11个回答
88
投票

你正在做的事情的替代方案:

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

另见BashFAQ/035.

注意:这也将匹配

--flags-off
,因为它是一个简单的子字符串检查。


17
投票

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


14
投票

您可以采用直接的方法,并迭代参数以测试每个参数是否与给定参数相等(例如

-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
),并重用它。封装规则!
但即使有命令,这种力量也会成为缺陷。如果你会一次又一次地使用它,你每次都会解析所有参数。

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


7
投票

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

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

getopt

这是一个独立的可执行文件,已经存在了很长时间。 旧版本缺乏处理引用参数的能力(foo a "this 不会工作” c) 和可以的版本,笨拙地这样做。如果你是 运行最新的 Linux 版本,您的“getopt”可以做到这一点;上合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

4
投票

我对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

4
投票
if [ "$1" == "-n" ]; then
    echo "Flag set";
fi

3
投票

Dennis Williamsonanswer的修改与简短形式的论证的附加示例。

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

它解决了误报匹配

--flags-off
甚至
--another--flag
的问题(对于单破折号参数更流行的这种情况:
--one-more-flag
for
*-f*
)。

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


1
投票

这是投票最多的答案的变体,不会发现误报

if [[ " $* " == *" -r "* ]]; then

0
投票

不是替代品,而是改进。

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

寻找 word boundaries 将确保真正获得选项

--flag
而既不是
--flagstaff
也不是
--not-really--flag


0
投票

另一个变化:

flag1="false"
if [[ " $@ " =~ " --flag1 " ]]; then
    flag1="true"
fi

flag2="false"
if [[ " $@ " =~ " --flag2 " ]]; then
    flag2="true"
fi

0
投票

Mb 像这样的东西:

if [[ ($* == -f) || ($* == --flag) ]];
then
  echo ">>>> Running with flag"
else
  echo ">>>> Running without flag"
fi
© www.soinside.com 2019 - 2024. All rights reserved.