【问题标题】:Bash: test mutual equality of multiple variables?Bash:测试多个变量的相互相等性?
【发布时间】:2012-02-07 09:14:22
【问题描述】:

测试几个变量是否都相等的正确方法是什么?

if [[ $var1 = $var2 = $var3 ]] # syntax error

有必要这样写吗?

if [[ $var1 = $var2 && $var1 = $var3 && $var2 = $var3 ]] # cumbersome
if [[ $var1 = $var2 && $var2 = $var3 && $var3 = $var4 ]] # somewhat better

不幸的是,我能找到的其他优秀的 Advanced Bash Scripting Guide 和其他在线资源没有提供这样的例子。

我的特别动机是测试几个目录是否都有相同数量的文件,使用ls -1 $dir | wc -l 来计算文件数。

注意
“var1”等是示例变量。我正在寻找任意变量名称的解决方案,而不仅仅是具有可预测数字结尾的变量名称。

更新
我已经接受了 Richo 的回答,因为它是最一般的。但是,我实际上使用的是 Kyle,因为它是最简单的,而且我的输入可以保证避免警告。

谢谢大家的建议。

【问题讨论】:

  • 您只需要对 N 个变量进行 N-1 次测试。如果 var1 和 var2 相等,并且 var2 和 var3 相等,那么您不必测试 var1 和 var3 是否相等。你有固定数量的变量吗?

标签: bash conditional


【解决方案1】:

如果你想测试任意数量的项目是否相等(我们称它们为 $item1-5,但它们可以是一个数组

st=0
for i in $item2 $item3 $item4 $item5; do
    [ "$item1" = "$i" ]
    st=$(( $? + st ))
done

if [ $st -eq 0 ]; then
    echo "They were all the same"
fi

【讨论】:

  • 如果您只想报告“它们都一样”,如果比较失败,您甚至可以break
  • 最外层的$ 是什么,为什么我们需要双括号?感谢您的回答。
  • 双括号做算术,$是返回值以便我们可以赋值给st
【解决方案2】:

如果它们是单个词,你可以得到非常便宜的。

varUniqCount=`echo "${var1} ${var2} ${var3} ${var4}" | sort -u | wc -l`
if [ ${varUniqCount} -gt 1 ]; then
   echo "Do not match"
fi

【讨论】:

    【解决方案3】:

    传递方法检查。

    #!/bin/bash
    
    var1=10
    var2=10
    var3=10
    
    if [[ ($var1 == $var2) && ($var2 == $var3) ]]; then 
        echo "yay"
    else
        echo "nay"
    fi
    

    输出:

    [jaypal:~/Temp] ./s.sh 
    yay
    

    注意:

    由于您在问题中声明您的目标是测试具有相同数量文件的多个目录,因此我想到了以下解决方案。我知道这不是你要求的,所以请随意忽略它。

    第一步:

    确定给定目录中的文件数。该命令也将查看子目录内部,但可以使用find-depth 选项进行控制。

    [jaypal:~/Temp] find . -type d -exec sh -c "printf {} && ls -1 {} | wc -l " \;
    .       9
    ./Backup       7
    ./bash       2
    ./GTP      22
    ./GTP/ParserDump      11
    ./GTP/ParserDump/ParserDump       1
    ./perl       7
    ./perl/p1       2
    ./python       1
    ./ruby       0
    ./scripts      22
    

    第二步:

    这可以与Step1 结合使用,因为我们只是将内容重定向到文件。

    [jaypal:~/Temp] find . -type d -exec sh -c "printf {} && ls -1 {} | wc -l " \; > file.temp
    

    第三步:

    使用以下命令,我们将在 file.temp 中查找两次,它将为我们提供具有相同数量文件的目录列表。

    [jaypal:~/Temp] awk 'NR==FNR && a[$2]++ {b[$2];next} ($2 in b)' file.temp file.temp | sort -k2
    ./GTP/ParserDump/ParserDump       1
    ./python       1
    ./bash       2
    ./perl/p1       2
    ./Backup       7
    ./perl       7
    ./GTP      22
    ./scripts      22
    

    【讨论】:

    • 谢谢,但我的实际变量的命名并不方便/模糊。我已更新问题以反映我正在寻找通用解决方案。
    • 解决方案不依赖于变量名;特别是,它没有利用他们的名字以数字结尾的事实。
    • 嗨@reve_etrange,我已经为你的主要目标添加了一个解决方案,因为这不是你要求的,请随意忽略它。
    • @KeithThompson 该评论与答案的先前迭代有关,该迭代使用结束字符上的范围将变量放入数组中,然后遍历该数组以执行测试。跨度>
    【解决方案4】:

    (已编辑以包含分隔符以解决 Keith Thompson 指出的问题)

    将变量值视为字符串,您可以将它们与合适的分隔符连接起来并进行比较:

    if [[ "$var1|$var2|$var3" = "$var1|$var1|$var1" ]]
    

    我使用 = 而不是 == 因为 == 不是 [[ ]] 内部的相等比较,而是模式匹配。

    【讨论】:

    • 如果它们是数字呢?标准文件计数技巧返回字符串,但原则上它们可以是数字。
    • 没关系。只要任何给定的数字总是以相同的方式输出,即 3 总是“3”或“03”或“003”,但有时不是“3”,有时不是“003”,那么比较应该有效。
    • 聪明,但是对于var2=abcvar2=abvar3=cabc. You can fix that by adding a delimiter between the strings that never appears n any of them (which may not always be possible), such as "$var1|$var2|$var3" = "$var1|$var1|$var1` .
    • 哈,不错的收获。对于仅涉及数字的简单情况,分隔符应该可以正常工作。
    • 您可能想要编辑您的答案;我的修改没有解决这个问题。
    【解决方案5】:

    对于您的具体情况,这应该有效:

    distinct_values=$(for dir in this_dir that_dir another_dir ; do ls -l "$dir" | wc -l ; done | uniq | wc -l)
    if [ $distinct_values -eq 1 ] ; then
        echo All the same
    else
        echo Not all the same
    fi
    

    解释:

    • ls -l "$dir" 列出目录中的文件和子目录,每行一个(省略点文件)。
    • 将输出通过wc -l 传递给您目录中的文件数。
    • 对列表中的每个目录连续执行此操作会为您提供一个列表,其中包含每个目录中的文件数;如果每个有 7 个,这将给出 3 行,每行包含数字 7
    • 通过uniq 管道消除连续的重复行。
    • 通过wc -l 连接 会为您提供不同行的数量,当且仅当所有目录包含相同数量的文件时,该数量才会为 1。

    请注意,第 4 阶段的输出不一定为您提供目录中不同数量的文件的数量; uniq 仅删除 相邻 重复项,因此如果输入为 7 6 7,则不会合并两个 7s。但如果它们都相同,它会将所有行合并为 1

    这就是 Unix 命令行的强大之处:将小工具放在一起做有趣和有用的事情。 (给我看一个可以做到这一点的 GUI!)

    对于存储在变量中的值,将第一行替换为:

    distinct_values=$(echo "$this_var" "$that_var" "$another_var" | fmt -1 | uniq | wc -l)
    

    这假定变量的值不包含空格。

    【讨论】:

    • 您能解释一下比较是如何进行的吗?真的应该是1(因为0 在bash 中是true)?
    • @reve_etrange:我已经用解释更新了答案; 1 只是一个数字,而不是真/假或成功/失败状态。 (顺便说一句,我不确定在 bash 中 0 是否是 true;更准确地说,它表示 成功。)
    • 谢谢,我现在明白了,这个答案可能是最酷的。如果可以的话,我会标记不止一个是正确的...我认为 Richo's 是最通用的,而 Kyle's 是最简单的...
    最近更新 更多