如何在 bash 脚本中为 if 条件组合 AND 和 OR 条件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38067413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 14:50:21  来源:igfitidea点击:

How to combine AND and OR condition in bash script for if condition?

bashunixif-statement

提问by sijo0703

I was trying to combine logical AND & OR in a bash script within if condition. Somehow I am not getting the desired output and it is hard to troubleshoot. I am trying to validate the input parameters passed to a shell script for no parameter and the first parameter passed is valid or not.

我试图在 if 条件下的 bash 脚本中结合逻辑 AND & OR。不知何故,我没有得到所需的输出,并且很难进行故障排除。我正在尝试验证传递给没有参数的 shell 脚本的输入参数,并且传递的第一个参数是否有效。

if [ "$#" -ne 1 ] && ([ "" == "ABC" ] || [ "" == "DEF" ] || [ "" == "GHI" ] || [ "" == "JKL" ]) 
then
echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"
exit 1
fi

Can anyone point out what is going wrong here?

谁能指出这里出了什么问题?

回答by mklement0

The immediate problem with your statement is one of logic: you probably meant to write:

您的陈述的直接问题是逻辑之一:您可能打算写:

if [ "$#" -ne 1 ] || ! ([ "" = "ABC" ] || [ "" = "DEF" ] || [ "" = "GHI" ] || [ "" = "JKL" ]) 
then
  echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2
  exit 1
fi

That is: abort, if either more than 1 argument is given OR if the single argument given does NOT equal one of the acceptable values.

即:中止,如果给出了 1 个以上的参数,或者如果给出的单个参数不等于可接受的值之一。

Note the !to negate the expression in parentheses and the use of the POSIX-compliant form of the string equality operator, =(rather than ==).

请注意!否定括号中的表达式以及使用符合 POSIX 形式的字符串相等运算符=(而不是==)。

However, given that you're using Bash, you can make do with a single [[ ... ]]conditional and Bash's regular-expression matching operator, =~:

但是,鉴于您使用的是 Bash,您可以使用单个[[ ... ]]条件和 Bash 的正则表达式匹配运算符=~

if [[ $# -ne 1 || !  =~ ^(ABC|DEF|GHI|JKL)$ ]] 
then
  echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2
  exit 1
fi

If POSIX compliance is not required, [[ ... ]]is preferable to [ ... ]for a variety of reasons. In the case at hand, $#and $1didn't need quoting, and ||could be used insidethe conditional.

如果不需要 POSIX 合规性,出于各种原因[[ ... ]]最好使用POSIX 。在手的情况下,并没有需要报价,并且可以使用里面的条件。[ ... ]$#$1||

Note that =~as used above works in Bash 3.2+, whereas the implicit extglobsyntax used in anubhava's helpful answerrequires Bash 4.1+;
in earlier versions you can, however, explicitly enable (and restore to its original value after) the extglobshell option: shopt -s extglob.

请注意,=~如上所用,在 Bash 3.2+ 中有效,而anubhava 的有用答案中extglob使用的隐式语法需要 Bash 4.1+; 但是,在早期版本中,您可以显式启用(并在之后恢复其原始值)shell 选项:。
extglobshopt -s extglob

回答by anubhava

BASH actually allows use of extended glob inside [[ ... ]]and have &&inside as well.

BASH 实际上允许在内部使用扩展的 glob[[ ... ]]并且也有&&内部。

So you can do:

所以你可以这样做:

if [[ $# -ne 1 &&  == @(ABC|DEF|GHI|JKL) ]]; then
   echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"
   exit 1
fi

回答by Taywee

A few things:

一些东西:

  • [...]in bash is equivalent to the same testcommand (check the man page), so those && and || are not logical operators, but rather the shell equivalents
  • Parentheses in POSIX shell are not a grouping operator. They will work here, but they open a subshell, you are better off using standard test options of -aand -o(making your if statement if [ "$#" -ne 1 -a \( "$1" == "ABC" -o "$1" == "DEF" -o "$1" == "GHI" -o "$1" == "JKL" \) ], though based on your logic, it sounds like you actually want something like if [ "$#" -ne 1 -o \( "$1" != "ABC" -a "$1" != "DEF" -a "$1" != "GHI" -a "$1" != "JKL" \) ]. You probably can get better results with a casestatement like follows:
  • [...]在 bash 中相当于相同的test命令(检查手册页),所以那些 && 和 || 不是逻辑运算符,而是 shell 等价物
  • POSIX shell 中的括号不是分组运算符。他们将在这里工作,但他们打开一个子shell,你是关闭更好地使用标准测试选项-a-o(使你的if语句if [ "$#" -ne 1 -a \( "$1" == "ABC" -o "$1" == "DEF" -o "$1" == "GHI" -o "$1" == "JKL" \) ],但根据你的逻辑,这听起来像你真正想要的东西一样if [ "$#" -ne 1 -o \( "$1" != "ABC" -a "$1" != "DEF" -a "$1" != "GHI" -a "$1" != "JKL" \) ]。你也许可以得到一个更好的结果case声明如下:


usage() {
    echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"
}


if [ "$#" -ne 1 ] 
then
    usage
    exit 1
fi

case "" in
    ABC)
        echo "Found ABC"
        ;;
    DEF)
        echo "Found DEF"
        ;;
    GHI)
        echo "Found GHI"
        ;;
    JKL)
        echo "Found JKL"
        ;;
    *)
        usage
        exit 1
        ;;
esac

If you want to pass a set of possible static arguments in, you might want to look at the getoptsspecial shell command.

如果您想传入一组可能的静态参数,您可能需要查看getopts特殊的 shell 命令。