前言:
Linux的常用命令应该不超过100个,因此,Linux的入门门槛是比较低的,但,高手和低手的区别在于一些其它的看起来很古怪的命令的认知和应用,比如,本文将要讲解的这个diff命令。
虽然,多会一些命令不会让你立刻成为一个Linux高手,但,从来高手都是比平常人站的更高,基础更加的牢固,因此眼界更远,能力更强,不是吗?
笑(^^^^_^^^^),因此,我现在也是万中无一的高手了。
OK, 不扯淡了,下面开始详细讲解这个非常有用的diff命令。
一,
diff命令的用途和历史
OK,大概的历史得从c语言开始了,这个时间就很久远了,大概得从5 60年代开始了,好了,具体的细节我也是没有太多的记忆了,但,上个世纪80年代初,加州大学伯克利分校推出 BSD版本的 Unix 时,觉得 diff 的显示结果太简单,最好加入上下文,便于了解发生的变动。因此,推出了上下文格式的 diff,也就是优化了diff命令的输出,从这一点看,该命令是非常的古老了。
diff命令是Unix系统下的内置命令,虽然非常的古老,但,该命令从来没有被删除,一直都存在于Unix系统内。因此,其实本文是旧酒装新瓶而已。
那么,该命令主要是比较文件(夹)的内容,并输出所比较的文件的内容哪些地方不同,或者哪些文件存在于文件夹,主要针对的是文本文件,二进制文件的比对工作用这个命令是不合适的,这里需要特别注意。
二,
diff命令的输出形式和常用参数
前文大略提了一下上下文格式的diff,那么,diff命令的输出到底有哪些呢?总共是四种格式:
正常格式(选项 --normal )
并列格式(选项 -y, --side-by-side)
上下文格式(选项 -C NUM, -c, --context[=NUM])
合并格式(选项 -U NUM, -u, --unified[=NUM])
常用参数:
c ,u, y,w,W,Z ,r
- -a, --text
所有的文件都视为文本文件来逐行比较
-B, --ignore-blank-lines
忽略插入删除空行引起的变化
-b, --ignore-space-change
忽略因空白符数量不同造成的差异
-C NUM
-c, --context[=NUM]
使用上下文格式输出,显示异行处上下指定数量的行(默认为 3 行)
--color[=WHEN]
将输出着色;WHEN 可取值 never、always 或 auto(默认值)
-D, --ifdef=NAME
输出与 "#ifdef NAME" 不同的合并文件
-d, --minimal
改变算法找出一组更小的变更。这会使 diff 变慢
-E, --ignore-tab-expansion
忽略因 Tab 扩展引起的更改
-e, --ed
输出为一个有效的 ed 脚本
-F, --show-function-line=RE
显示匹配 RE 的前面的行
--from-file=FILE1
将 FILE 1 与所有文件进行比较;FILE 1 可以是一个目录
--GTYPE-group-format=GFMT
用组格式 GFMT 格式化类型为 GTYPE 的输入组
--line-format=LFMT
用格式 LFMT 格式化所有输入行
--LTYPE-line-format=LFMT
用行格式 LFMT 格式化类型为 LTYPE 的输入行
上面三个选项和响应的格式提供了对输出的细粒度控制。
行类型 LTYPE 可取值 old、new 或 unchanged,组类型 GTYPE 可取值 LTYPE 或 changed。
组格式 GFMT 特含如下内容:
%< FILE1 中的行
%> FILE2 中的行
%= FILE1 和 FILE2 中共有的行
%[-][WIDTH][.[PREC]]{doxX}LETTER
使用 printf 输出风格修饰 LETTER,LETTER 使用如下字母表示新组,下面的小写字母表示旧组
F 首行行号
L 尾行行号
N 行数 = L-F+1
E 等于 F-1
M 等于 L+1
%(A=B?T:E)
if A equals B then T else E
行格式 LFMT 特含如下内容:
%L 行的内容
%l 行的内容,不包括任何尾随的换行符
%[-][WIDTH][.[PREC]]{doxX}n
使用 printf 风格修饰输入行号 n
组格式 GFMT 和行格式 LFMT 共有的内容:
%% 表示百分号 %
%c'C' 表示大写字母 C
%c'\OOO' 表示码值为八进制 000 的字符
C 其他字符
--help
显示帮助信息并退出
--horizon-lines=NUM
保持共有前缀和后缀的 NUM 行
-I, --ignore-matching-lines=RE
忽略匹配正则表达式 RE 的行
-i, --ignore-case
忽略大小写
--ignore-file-name-case
比较文件名时忽略大小写
-l, --paginate
将结果交由 pr 程序来分页
--label LABEL
输出比较结果时使用 LABEL 代替文件名和时间戳
--left-column
只输出公共行的左列
-N, --new-file
将缺席文件视为空文件。在比较目录时,若文件 A 仅出现在某个目录中,预设会显示:Only in 目录:文件 A。若使用 -N 参数,则 diff 会将文件 A 与一个空白的文件比较
-n, --rcs
将比较结果以 RCS 的格式来显示
--no-dereference
不解析
--no-ignore-file-name-case
比较文件名时大小写敏感
--normal
使用正常格式输出比较结果。为默认输出格式
-p, --show-c-function
显示每个更改在哪个 C 函数中
--palette=PALETTE
当使用选项 --color 时,指定要使用的颜色。PALETTE 是使用冒号分隔的终端支持的能力列表
-q, --brief
仅报告文件是否相异,忽略差别的细节
-r, --recursive
当比较目录时,递归比较子目录
-S, --starting-file=FILE
当比较目录时,由 FILE 开始。这用于继续中断的比较
-s, --report-identical-files
当两个文件相同时报告
--speed-large-files
使用启发规则加速操作那些有许多离散的小差异的大文件
--strip-trailing-cr
去掉输入行尾的回车符 CR
--suppress-common-lines
在并列格式中不印出公共行
-T, --initial-tab
在每行前面加上 Tab 以便对齐
-t, --expand-tabs
在输出时将 Tab 扩展为空格
--tabsize=NUM
一个 Tab 表示 NUM(默认 8) 个空格
--to-file=FILE2
将所有文件与 FILE2 进行比较;FILE2 可以是一个目录
-U NUM
-u, --unified[=NUM]
使用合并格式输出,输出 NUM(默认 3)行的统一上下文
--unidirectional-new-file
将缺席的第一批文件视为空文件
-v, --version
输出版本信息并退出
-W, --width=NUM
使用 -y 选项采用列格式输出时,指定栏宽。缺省为 130
-w, --ignore-all-space
在比较行的时候忽略空白符
-y, --side-by-side
使用并格式输出两列
-Z, --ignore-trailing-space
忽略行尾的空白符
三,
正常模式
本文将使用两个相似度比较高的文本文件做示例,文件内容如下:
A文件:
[root@centos61 media]# cat -A -n index.php 1 <?php$ 2 /**$ 3 * Front to the WordPress application. This file doesn't do anything, but loads$ 4 * wp-blog-header.php which does and tells WordPress to load the theme.$ 5 *$ 6 * @package WordPress$ 7 */$ 8 $ 9 /**$ 10 * Tells WordPress to load the WordPress theme and output it.$ 11 *$ 12 * @var bool$ 13 */$ 14 define( 'WP_USE_THEMES', true );$ 15 hello World!!!!!~~$ 16 /** Loads the WordPress Environment and Template */$ 17 require( dirname( __FILE__ ) . '/wp-blog-header.php' );$
B文件:
[root@centos61 media]# cat -A -n index.php.back 1 <?php$ 2 /**$ 3 * Front to the WordPress application. This file doesn't do anything, but loads$ 4 * wp-blog-header.php which does and tells WordPress to load the theme.$ 5 *$ 6 * @package WordPress$ 7 */$ 8 $ 9 /**$ 10 * Tells WordPress to load the WordPress theme and output it.$ 11 *$ 12 * @var bool$ 13 */$ 14 define( 'WP_USE_THEMES', true );$ 15 $ 16 $ 17 $ 18 hello World!!!!!~~ $ 19 /** Loads the WordPress Environment and Template */$ 20 require( dirname( __FILE__ ) . '/wp-blog-header.php' );$
根据cat命令,我们肉眼观察,可以看到,是11行不同,B文件多了15 16 17 行, 18行的内容多了几个空格, 那么,diff命令会怎么说呢?
[root@centos61 media]# diff index.php index.php.back 11c11 < * --- > * 15c15,18 < hello World!!!!!~~ --- > > > > hello World!!!!!~~
OK,这个输出很简单,大部分人应该是看不懂了。那么,具体的含义是什么呢?
1,11c11
这个表示的是A文件的11行改变了,对应的是B文件的11行
2,< 这个表示方向,也就是指的是A文件, 下面的 > 同样是表示方向,指的是B文件,因为,diff命令是 diff A B 嘛
3,--- 是第一部分不同,简单说就是一个分隔的,因此,前面四行表示 11行的A文件比B文件少一个空格
4,15c15,18 说的是 15行到18行两个文件不同,具体的不同是A的15行内容在18行了,并且B多了三个空行,是15-18
A文件修改成如下:
[root@centos61 media]# cat -A -n index.php 1 <?php$ 2 /**$ 3 * Front to the WordPress application. This file doesn't do anything, but loads$ 4 * wp-blog-header.php which does and tells WordPress to load the theme.$ 5 *$ 6 * @package WordPress$ 7 */$ 8 $ 9 /**$ 10 * Tells WordPress to load the WordPress theme and output it.$ 11 *$ 12 * @var bool$ 13 */$ 14 define( 'WP_USE_THEMES', true );$ 15 hello World!!!!!~~$ 16 /** Loads the WordPress Environment and Template */$ 17 require( dirname( __FILE__ ) . '/wp-blog-header.php' );$
B文件修改成如下:
[root@centos61 media]# cat -A -n index.php.back 1 <?php$ 2 /**$ 3 * Front to the WordPress application. This file doesn't do anything, but loads$ 4 * wp-blog-header.php which does and tells WordPress to load the theme.$ 5 *$ 6 * @package WordPress$ 7 */$ 8 $ 9 /**$ 10 * Tells WordPress to load the WordPress theme and output it.$ 11 *$ 12 * @var bool$ 13 */$ 14 define( 'WP_USE_THEMES', true );$ 15 1111$ 16 $ 17 $ 18 hello World!!!!!~~ 111$ 19 require( dirname( __FILE__ ) . '/wp-blog-header.php' );$ 20 1111111111111222222222$ 21 333333333333333333$
再次比较:
[root@centos61 media]# diff index.php index.php.back 11c11 < * --- > * 15,16c15,18 < hello World!!!!!~~ < /** Loads the WordPress Environment and Template */ --- > 1111 > > > hello World!!!!!~~ 111 17a20,21 > 1111111111111222222222 > 333333333333333333
从整体的输出结果来看,通过数字和字母(例如11c11,15,16c15,18)分为了三个部分:
- 11c11
< *
---
> *
这里是表示左边的文件的11行是 一个星,右边的文件的11行是一个空格+一个星
- 15,16c15,18
< hello World!!!!!~~
< /** Loads the WordPress Environment and Template */
---
> 1111
>
>
> hello World!!!!!~~ 111
这里表示左边的文件的15 16行的内容改变为右边的15-18行的四行
- 17a20,21
> 1111111111111222222222
> 333333333333333333
这里表示左边的文件的17行后面添加右边的文件的20 21行内容即可,也就是 左边的文件加下面的内容(那些数字)到20 21行就会和右边的文件一样。
输出指出,11行,和15,18c15,16有改变,A文件比B文件少20和21行。那么,比较顺序改变一下,输出也会改变:
[root@centos61 media]# diff index.php.back index.php 11c11 < * --- > * 15,18c15,16 < 1111 < < < hello World!!!!!~~ 111 --- > hello World!!!!!~~ > /** Loads the WordPress Environment and Template */ 20,21d17 < 1111111111111222222222 < 333333333333333333
20,21d17表示左边的文件删除下面的内容(那些数字),并只有17行就会和右边的文件一致
四,
上下文模式----Context 模式
添加参数c即为Context 模式,上下文模式大体如下(仍然使用上面修改后的示例文件):
[root@centos61 media]# diff -c index.php index.php.back *** index.php 2023-05-13 23:22:41.638404080 +0800 --- index.php.back 2023-05-14 00:23:43.814240804 +0800 *************** *** 8,17 **** /** * Tells WordPress to load the WordPress theme and output it. ! * * @var bool */ define( 'WP_USE_THEMES', true ); ! hello World!!!!!~~ ! /** Loads the WordPress Environment and Template */ require( dirname( __FILE__ ) . '/wp-blog-header.php' ); --- 8,21 ---- /** * Tells WordPress to load the WordPress theme and output it. ! * * @var bool */ define( 'WP_USE_THEMES', true ); ! 1111 ! ! ! hello World!!!!!~~ 111 require( dirname( __FILE__ ) . '/wp-blog-header.php' ); + 1111111111111222222222 + 333333333333333333
结果分析如下:
第一部分的两行,显示两个文件的基本情况:文件名和时间信息。
*** index.php 2023-05-13 23:22:41.638404080 +0800
--- index.php.back 2023-05-14 00:23:43.814240804 +0800星号表示A文件,---表示B文件
第二部分
*** 8,17 ****表示A文件的8到17行
--- 8,21 ----表示B文件的8到21行
- 出现在前者,表示后者比前者少一行
+ 出现在后者,表示后者比前者多一行
! 出现在两者,表示有差别的行
OK,上下文模式的输出比正常模式的输出好像要更容易看懂。
五,
合并模式----也即 Unified 模式
这种模式是简化了输出,尤其是两个文件相似度很高的情况下,会省去很多输出,输出更为简洁,但还是不直观,使用参数u即可
[root@centos61 media]# diff -u index.php index.php.back --- index.php 2023-05-13 23:22:41.638404080 +0800 +++ index.php.back 2023-05-14 00:23:43.814240804 +0800 @@ -8,10 +8,14 @@ /** * Tells WordPress to load the WordPress theme and output it. - * + * * @var bool */ define( 'WP_USE_THEMES', true ); -hello World!!!!!~~ -/** Loads the WordPress Environment and Template */ +1111 + + +hello World!!!!!~~ 111 require( dirname( __FILE__ ) . '/wp-blog-header.php' ); +1111111111111222222222 +333333333333333333
输出说明:
第一部分,也是文件的基本信息。— 表示第一个文件,+++ 表示第二个文件。"---"表示变动前的文件,"+++"表示变动后的文件。
第二部分,@@包围的内容,其中 -8,10 表示输出的内容属于第一个文件的 8 至 10 行,+8,14 表示输出的内容属于第二个文件的 8 至 14行。
第三部分,为比较后合并的内容。减号 - 表示后者比前者少了该行,加号表示后者比前者多了该行。参数u后面其实是有默认的3,也就是说输出默认的三行的上下文,如果想要更改,比如,只1行的上下文,命令为diff -u1 index.php index.php.back
[root@centos61 media]# diff -u1 index.php index.php.back
--- index.php 2023-05-13 23:22:41.638404080 +0800
+++ index.php.back 2023-05-14 00:23:43.814240804 +0800
@@ -10,3 +10,3 @@
* Tells WordPress to load the WordPress theme and output it.
- *
+ *
* @var bool
@@ -14,4 +14,8 @@
define( 'WP_USE_THEMES', true );
-hello World!!!!!~~
-/** Loads the WordPress Environment and Template */
+1111
+
+
+hello World!!!!!~~ 111
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
+1111111111111222222222
+333333333333333333
六,
并列模式
参数y结合-W 指定输出宽度(默认是130的间距,这个间距指的是左右的结果之间的距离)
[root@centos61 media]# diff -y -W 200 index.php index.php.back <?php <?php /** /** * Front to the WordPress application. This file doesn't do anything, but loads * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * wp-blog-header.php which does and tells WordPress to load the theme. * * * @package WordPress * @package WordPress */ */ /** /** * Tells WordPress to load the WordPress theme and output it. * Tells WordPress to load the WordPress theme and output it. * | * * @var bool * @var bool */ */ define( 'WP_USE_THEMES', true ); define( 'WP_USE_THEMES', true ); hello World!!!!!~~ | 1111 /** Loads the WordPress Environment and Template */ | > > hello World!!!!!~~ 111 require( dirname( __FILE__ ) . '/wp-blog-header.php' ); require( dirname( __FILE__ ) . '/wp-blog-header.php' ); > 1111111111111222222222 > 333333333333333333
说明:
- "|"表示前后2个文件内容有不同
- "<"表示后面文件比前面文件少了1行内容
- ">"表示后面文件比前面文件多了1行内容
七,
比较目录
目录比较的时候比较简单,ONLY in 仅存在的意思
[root@centos61 media]# diff -r /etc/ /var/ Only in /etc/: adjtime Only in /var/: adm Only in /etc/: aliases Only in /etc/: aliases.db Only in /etc/: alternatives Only in /etc/: anacrontab Only in /etc/: asound.conf Only in /etc/: audisp
总结:
根据实际的使用经验来说,diff命令推荐优先使用并列模式,最为直观,其次是普通模式,再次是上下文模式,最后是统一模式。