【问题标题】:xor conditional in bashbash中的xor条件
【发布时间】:2021-11-01 16:54:32
【问题描述】:

我想看看是否设置了两个变量中的任何一个,但不是在 bash 中都设置了。

我有类似的东西,但我不确定语法。我知道 xor 运算符不合逻辑,应该按位使用,但是我如何将 [ -z "$tag" ] 解析为 1 或 0 而不是 true 或 false

if [ -z "$tag" ] ^ [ -z "$flag" ]; then
  echo yes
else 
  echo no
fi

【问题讨论】:

  • bash 中没有^ 运算符。
  • @melpomene:当然有。它是按位异或(但你是对的,没有逻辑异或)。

标签: bash xor


【解决方案1】:
! testA; TESTA=$?
! testB; TESTB=$?

if [ $TESTA -ne $TESTB ]; then
    echo 'Exactly one of testA or testB was true -- the other was not'
fi

testAtestB 可以是任意程序/管道,例如[ -z "$tag" ][ -z "$flag" ]。重要的部分是用! 前缀反转返回代码,以便将0 转换为11-255 转换为0,以便将可能的退出代码集减少为二进制01。然后您可以安全地检查两者是否不同[ … -ne … ]

【讨论】:

  • 我喜欢这个!它概括而简洁! (和便携式)
  • 这正是我想要的,谢谢。简单易写。
【解决方案2】:

我能想到的最好的东西:

if [ "${tag:+A}" != "${flag:+A}" ]; then
    echo yes
else
    echo no
fi

如果foo 不为空,则"${foo:+bar}" 扩展为bar,否则为空字符串。

"${tag:+A}"$tag的值进行归一化处理:如果为空,则保持为空;如果不为空,则变为A

xor 只是布尔值上的不等式。这里我们对规范化的字符串使用不等式,这是由[!= 运算符提供的。


对于具有两个任意命令 foobar(不仅仅是 is-string-empty 测试)的通用解决方案,您可以使用以下命令:

if if foo; then ! bar; else bar; fi; then
    ...
else
    ...
fi

它看起来有点糟糕,你需要重复两次bar,但我想不出更简洁的表达方式。

您可以预先运行这两个命令并将它们的退出状态保存在变量中,这将删除 bar 重复,但会使代码整体更长。

【讨论】:

    【解决方案3】:

    感谢melpomene 的回答,我找到了一种方法可以将此逻辑推广到任意数量的变量 例如,如果你想检查$varA xor $varB xor $varC 您可以执行以下操作:

    SIMULATE_XOR="${varA:+A}${varB:+A}${varC:+A}"
    if [[ "${SIMULATE_XOR}" != "A" ]]; then 
        echo "you must set one, and only one variable"
    fi
    

    【讨论】:

      【解决方案4】:

      melpomene 的解决方案的启发,我将他的关键 if 语句放在一个函数中,但是要使用带引号的参数的函数,我们需要 eval 让 shell 执行所有扩展在执行语句之前 的位置参数上是必需的:

      function xor { eval "if $1; then ! $2; else $2; fi"; }

      这样,我们可以传递任意命令,例如。 G。 xor '[ -z "$PATH" ]' "find ~ -maxdepth 1 -name '.bash*'"

      编辑:

      此函数将命令范围缩小到[[ <unary test op> <operand> ]] 测试,但接受任意数量的操作数:

      function exactlyOne # Args: <unary test op> operands...
      {
          local operator=$1; shift
      
          local operand1 operand2
      
          if (($# == 2)); then
              operand1=$1
              operand2=$2
              shift 2
      
              if eval [[ $operator \$operand1 ]]; then ! eval [[ $operator \$operand2 ]]; else eval [[ $operator \$operand2 ]]; fi
          else # Recurse
              operand1=$1; shift
      
              if eval [[ $operator \$operand1 ]]; then ! exactlyOne $operator "$@"; else exactlyOne $operator "$@"; fi
          fi
      }
      

      测试 e。 G。如果只有一个变量被赋值,只需写
      exactlyOne -n "$a" "$b" "$c" "$d" ...

      【讨论】:

      • 不妨在evals 中使用\$operator 来防止shell 将变量内容拆分为空格。这样更安全。
      【解决方案5】:

      为了回答最初的问题,我认为这个简单的解决方案可以完成工作:

      exclusive_or() {
          [[ -n "$1" && -z "$2" ]]
      }
      if exclusive_or "$tag" "$flag"; then
          echo yes
      else
          echo no
      fi
      

      因为如果未设置“tag”并设置了“flag”,则函数中的第一个参数将设置为 $flag。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-23
        • 2011-05-03
        • 1970-01-01
        • 2012-02-04
        • 1970-01-01
        • 1970-01-01
        • 2021-02-17
        • 2018-03-20
        相关资源
        最近更新 更多