Diff 输出“二进制文件 不同”,但不是通常的详细输出

Diff 输出“二进制文件 不同”,但不是通常的详细输出

我想使用命令比较两个文件 - “orienv”和“currenv” diff

我创建这两个文件的方式如下:

  1. 创建“currenv”文件

    $cat /proc/1/environ >> currenv
    $cat /pcoc/279/environ >> currenv
    $cat /proc/295/environ >> currenv
    //295 is the pid of the current console
    
  2. 创建 orienv 文件

    $printenv > orienv
    

然后我打电话diff如下

diff -u orienv currenv

并得到以下输出

二进制文件 orienv 和 currenv 不同

我期待diff带有标志的正常输出-u(例如,显示其中差异的输出)帅哥,指示哪个文件具有另一个文件没有的信息。

什么地方出了错?

答案1

环境变量的值可以包含换行符。在 中,变量由空字节分隔,空字节不能出现在环境变量的值或名称中。 (这并非巧合,这就是环境在内存中的表示方式。)当 diff 看到空字节时,它确定该文件是二进制文件(根据定义这是正确的:文本文件不包含空字节)并放弃显示差异,因为对于大多数二进制格式, diff 打印出来的内容是没有用的。/proc/PID/environ

您可以告诉 diff 继续并将文件视为文本diff --text,但如果您在两个文件之间执行此操作,则可能会显示仅包含一两个大更改行的显示,因为实际上环境并不包含许多换行符。 Diff 仅适用于以换行符分隔的行。如果您在 的内容和 的输出之间执行此操作,则表示一切都已更改,因为使用换行符作为分隔符。/proc/PID/environ/proc/PID/environprintenvprintenv

要获得有用的输出,请将空字节转换为换行符。这样每个环境变量都会从一行的开头开始。

diff -u orienv --label=currenv <(tr '\0' '\n' <currenv)

为了获得有用且明确的输出,还将换行符转换为空字节,以便每个环境变量都位于自己的单行上。然后,如果 diff 输出中存在空字节,则表明原始文件在该位置包含换行符。

diff -u --text --label=currenv <(tr '\0\n' '\n\0' <currenv1) --label=currenv <(tr '\0\n' '\n\0' <currenv2)

答案2

/proc/*/environ 不是文本文件。使用字符串:

strings /proc/{1,279,295}/environ >> currenv
env > orienv
diff -u orienv currenv

答案3

问题是因为/proc/<pid>/environ包含空字节。您可以通过 来查看表示为文本的不可打印字节cat -v,例如您可以^@在下面的输出中看到表示空字节:

$ cat -v /proc/20148/environ 
CLUTTER_IM_MODULE=xim^@COLORFGBG=15;0^@COLORTERM=truecolor^@ ...cont.

尽管如此,打印环境变量时仍需要考虑三个常见问题:

  1. ANSI 颜色转义码值将在终端上打印为“颜色”而不是文字,例如export p_red=$(tput setaf 1)。这会导致您的 diff 输出不需要的颜色,并且还会丢失颜色代码值。diff --color=always由于颜色中断,也将无法按预期工作。
  2. 函数定义通常包含多行。而且变量值也可能包含多行。例如:

    $ cat /etc/environment love=" 1st line 2nd line"

  3. 没有sort首先进行比较会得到令人困惑的结果。

您可以通过以下方式解决所有这些问题:

$ sudo cat /proc/1/environ | tr '\n\0' ' \n' | cat -v > currenv
$ sudo cat /proc/279/environ | tr '\n\0' ' \n' | cat -v >> currenv
$ sudo cat /proc/295/environ | tr '\n\0' ' \n' | cat -v >> currenv
$ sort -u currenv -o currenv
$ printenv -0 | tr '\n\0' ' \n' | cat -v | sort -u > orienv
$ diff --unified="$(cat currenv orienv | wc -l)" orienv currenv --color=always

输出将是这样的:

...
 LOGNAME=xiaobai
 love= 1st line 2nd line
..
 PKG_CONFIG_PATH=:/usr/lib/pkgconfig:/usr/local/lib/pkgconfig
 p_lblue=^[[38;5;50m
 p_lgreen=^[[38;5;118m
 p_lred=^[[38;5;196m
 p_orig=^[(B^[[m
 p_red=^[[31m
 PROFILEHOME=
...
 QT_IM_MODULE=ibus
+recovery=
+rootmnt=/root
 S_COLORS=auto

说明:

  1. tr '\n\0' ' \n'表示 的缩写形式tr '\n' ' ' | tr '\0' '\n'。我们先将换行符(即多行值)替换\n为空' ',然后将空字节替换\0为换行符'\n'。我们无需担心多行值或空字节的歧义,因为我们首先用空格替换多行值。
  2. printenv可以-0选择每个输出行以 NUL 结束,而不是换行符。的输出printenv -0将与 的行为相同/proc/<pid>/environ。所以我们也可以做同样的事情tr '\n\0' ' \n'
  3. sort -u进行排序,并删除重复的行,以避免>> currenv不断附加相同的变量而很难看出差异。
  4. 我们将统一值设置为 valuecat currenv orienv | wc -l来查看所有可能的统一值。diff工具实际上调用strtoumax()来转换该值,因此如果传递负值,您将得到垃圾值。
  5. cat -v将颜色代码转换为可打印文本。我们无需担心cat -v需要单独的空字节和颜色代码,因为我们已经tr '\n\0' ' \n'首先替换了所有空字节。
  6. 最后,diff --color=always用色块(绿色/红色/白色)打印差异。我们无需担心颜色代码会中断差异颜色,因为cat -v已经转换了颜色代码。

相关内容