在bash中比较两个变量的内容

66

我有一个bash脚本中的变量$data和变量$file

data=$(echo "$(printf '%s\n' "${array[@]/%/$'\n\n'}")")
file=$(<scriptfile_results)

这些变量将包含文本。如何比较这两个变量?一种选项是使用 diff(1) 实用程序,例如:

diff -u <(echo "$data") <(echo "$file")

这是比较两个变量内容的正确而优雅的方式吗?此外,<( ) 技术被称为什么?据我所知,每个 <( ) 都会创建一个临时文件(命名管道)。

5个回答

120

是的,diff <(echo "$foo") <(echo "$bar") 是可以的。

通过在bash manpage中搜索 <( 字符,您可以发现这被称为“进程替换”。

您不需要担心创建临时文件的效率,因为临时文件实际上只是一个管道,而不是磁盘上的文件。试试这个:

$ echo <(echo foo)
/dev/fd/63

这表明临时文件实际上就是管道 "文件描述符63"。尽管它出现在虚拟的/dev文件系统中,但磁盘从未被触及。
你可能需要担心的实际效率问题是“进程替代”的“进程”部分。Bash会fork另一个进程来执行echo foo。在某些平台上,例如Cygwin,如果经常执行此操作,可能会非常慢。然而,在大多数现代平台上,forking速度相当快。我刚刚尝试了运行脚本来同时进行1000个进程替换:
echo <(echo foo) <(echo foo) ... 997 repetitions ... <(echo foo)

在我的旧款 Mac 笔记本电脑上,用了 0.225 秒,在同一台笔记本电脑上运行的 Ubuntu 虚拟机中,用了 2.3 秒。通过将次数除以 1000,可以看出进程替换所需时间不到 3 毫秒——这完全被 diff 的运行时间所掩盖,可能不是你需要担心的事情!

1
谢谢您的解释!在我的Xeon X3220@2.40GHz上,'time for i in {0..1000}; do echo <(echo foo); done'花费了0.264秒。 - Martin
3
请注意,即使是 echo foo > foo; cat foo; rm foo,也不太可能接触到磁盘。如果在虚拟文件系统刷新到物理分区之前删除了临时文件,则永远不会写入硬盘。 - William Pursell
你的回声需要在第一行加上“-e”。感谢你的答案! - thebunnyrules
@thebunnyrules 我认为 -e 在这里并没有帮助,因为它会使字面字符串 foo\tbarfoo bar 相等,尽管它们是不同长度的不同字符串。你有什么例子吗? - andrewdotn
@andrewdotn,我曾错误地认为需要在变量中扩展换行符,但昨天我尝试了一下echo命令,发现并不需要。只有当用户添加了换行符和制表符时(如\n或\t),才需要进行扩展,而变量通过var = $(某些操作)定义时继承的换行符则不需要。如果我可以撤回编辑,我会这样做,但一旦提交就超出了我的控制范围。请随意拒绝它。 - thebunnyrules
@thebunnyrules 不用担心,很高兴你解决了这个问题。在bash变量中获取一个原始的换行符的简单方法是使用多行字符串文字;在第1行,foo="bar,然后在第2行,baz" - andrewdotn

7
test "$data" = "$file" && echo the variables are the same

如果您希望详细说明,也可以这样做:

if test "$data" = "$file"; then
  : variables are the same
else
  : variables are different
fi

1
有没有一种方法可以在不将它们写入临时文件的情况下,实际上对两个变量而不是两个文件使用 diff?我以前就想过这个问题。(这样你就可以看到它们之间的区别) - sampson-chen
据我所知,目前没有。但是,如果有一些经验丰富的Bash程序员确认了这一点,那将是很好的。 - Martin

6

以下是我最佳的解决方案:

var1="cat dog mule pig"
var2="cat dog ant"

diff <( echo "$var1" ) <( echo "$var2" )

首先我设置了var1和var2。然后使用<( )元素进行差分,表示输入是一个变量。


3
~ cat test.sh   

#!/usr/bin/env bash

array1=(cat dog mule pig)
array2=(cat dog ant)

diff -ia --suppress-common-lines <( printf "%s\n" "${array1[@]}" ) <( printf "%s\n" "${array2[@]}" )

你可以操作结果,创建变量等等。我的版本会打印出一个差异,例如。试一下,不会有任何损害。 - deadElk

3

使用 comm 命令:

comm -23 <(echo $variableA | tr ' ' '\n' | sort) <(echo $variableB | tr ' ' '\n' | sort)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接