【问题标题】:How to combine AND and OR condition in bash script for if condition?如何在 bash 脚本中为 if 条件组合 AND 和 OR 条件?
【发布时间】:2016-10-30 06:16:54
【问题描述】:

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

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

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

【问题讨论】:

    标签: bash unix if-statement


    【解决方案1】:

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

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

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

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

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

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

    如果不需要 POSIX 合规性,对于 a variety of reasons[[ ... ]] 优于 [ ... ]。 在本例中,$#$1 不需要引用,|| 可以在内部条件使用。

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

    【讨论】:

      【解决方案2】:

      一些事情:

      • 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 "$1" in
          ABC)
              echo "Found ABC"
              ;;
          DEF)
              echo "Found DEF"
              ;;
          GHI)
              echo "Found GHI"
              ;;
          JKL)
              echo "Found JKL"
              ;;
          *)
              usage
              exit 1
              ;;
      esac
      

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

      【讨论】:

      • 好点;狡辩:POSIX spec. for test 本身警告不要使用 -a-o():“XSI 扩展指定 -a 和 -o 二进制初级和 '(' 和 ')' 运算符已被标记过时的。(许多使用它们的表达式由语法模糊定义,具体取决于正在评估的特定表达式。)“
      • 这是一个很好的狡辩。您将如何仅使用 POSIX 交替执行此操作?命令组 ({...})、&& 和 ||?
      • @Taywee:正确,你会使用[ ... ] && { [ ... ] || [ ... ]; }
      • 您还可以将case 语句缩短为ABC|DEF|GHI|JKL) echo "Found $1" ;;
      • @chepner 我知道,我在假设他们可能会做不同的事情并且可能想要独立处理。
      【解决方案3】:

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

      所以你可以这样做:

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

      【讨论】:

      • 是的,这也可以,但这个更短,因为它只有一个 [[ ... ]] 表达式
      • 但是我认为在这段代码中应该是 $1 != ... 但我只是复制了 OP 的条件
      • ++,但请注意隐式 extglob 语法仅适用于 Bash 4.1+。
      • 在 4.1 之前,您只需要使用 shopt -s extglob 显式启用它。
      猜你喜欢
      • 1970-01-01
      • 2012-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      • 2014-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多