1. 程式人生 > >Linux中的Diff和Patch

Linux中的Diff和Patch

manual bubuko 運算 tdi uid bash span 錯誤 aca

轉自:https://www.cnblogs.com/cocowool/p/6409643.html

本文主要記錄兩個命令的學習情況:diff 和 patch。diff 和 patch 是一對工具,使用這對工具可以獲取更新文件與歷史文件的差異,並將更新應用到歷史文件上。在數學上說,diff就是對兩個集合的差運算,patch就是對兩個集合的和運算。

簡單的例子

使用這個例子來說明如何進行文件的對比和打補丁。

這裏有兩個文件 original.txt 和 updated.txt,如下:

#include <stdio.h>

function old(){
        printf("This is a file\n");

        return 0;
}
#include stdio.h

function new(){
        printf("This is b file\n");
        return 0;
}

執行 diff original.txt updated.txt的結果為

技術分享圖片

下面先對結果中出現的一些符號做一些解釋。1,4c1,這個內容輸出實際上是給patch看的,表示告訴patch在original.txt文件中的1到4行應當被updated.txt中的內容替換,替換的內容是updated.txt的第1行。這裏可能會出現三個字母表示不同的意義,分別是c表示更新、a表示追加、d表示刪除。

c表示在original文件中的m,n行的內容將要被updated文件中的內容替換。
a表示追加,這時左邊的數字只能是一個數字,而不會是一個範圍,表示向original文件中追加右側數字表示內容。
d表示刪除。左側的數字可能是一個範圍,表示要刪除的內容,右側是一個數字,表示如果沒有被刪除應該出現在updated文件的什麽位置。也許有人覺得後邊的數字是多余的,保留這個數字是因為補丁可以反向使用。
<表示patch應當將這個標誌後面的內容刪除。
>表示patch應當將這個標誌後面的內容添加。

了解了diff的輸出結果,該給original文件創建補丁了。補丁實際上就是diff的輸出結果,我們可以直接將輸出結果保存成文件,也可以使用管道符號做這件事,如下:

diff original.txt updated.txt > mypatch.patch

這時我們就有了一個補丁文件,可以將original文件更新了updated文件了。

patch original.txt -i my patch.patch -o updated-1.txt

這個命令會生成一個新的文件,可以看到這個我們之前的update.txtw文件一模一樣。

上下文補丁

觀察之前diff給出的結果樣式,對於需要替換的位置,僅僅給出了行號,如果文件突然新增了一個空行,補丁應用的時候就會發生問題。另外一種情況,如果將補丁文件應用到了一個錯誤的源文件上,假如恰好這個文件有同樣的行數,那麽補丁也可以成功應用。而這都是我們不希望看到的結果。幸好,diff提供了一種不同的結果樣式來避免上面的這些問題。

diff -c original.txt updated.txt

技術分享圖片

比較結果中包含了文件名,這樣我們在應用補丁的時候,就不用輸入文件名,從而節省了時間,避免了文件名輸入錯誤的可能。文件名後都跟著文件的修改時間。再往下就是15個星號 * 表示後面的內容為文件替換、更新、刪除等。*-包含的數字或者數字範圍表示行號,!開始的內容表示需要替換的內容,-表示需要刪除的內容,表示需要增加的內容,patch會依據這個上下文關系對文件進行更新。

patch -i mypatch2.patch -o updated.txt

註意,這裏如果不指定輸出文件的話,源文件就會被更新(這本來就是補丁文件的作用)。通常我們都會對源文件應用補丁,通常需要對多個文件進行處理。

比較多個文件並應用補丁

比較多個文件最簡單的辦法就是直接在命令後面跟文件夾,例如,如果包含子文件夾,記得加上 -r 參數。

diff originaldirectory updateddirectory 

技術分享圖片

也可以看看上下文比較的結果

RousseaudeMacBook-Pro:diff rousseau$ diff -c original update
diff -c original/function.txt update/function.txt
*** original/function.txt     Fri Feb 17 09:41:26 2017
--- update/function.txt     Fri Feb 17 09:42:06 2017
***************
*** 1,5 ****
! #includ <stdio.h>

  function main(){
       return 1;
  }
--- 1,8 ----
! #include <stdio.h>
! #include <stdlib.h>

  function main(){
+      printf("This is function main\n");
+
       return 1;
  }
diff -c original/original.txt update/original.txt
*** original/original.txt     Fri Feb 17 09:40:29 2017
--- update/original.txt     Fri Feb 17 09:40:51 2017
***************
*** 1,9 ****
  #include <stdio.h>

! function old(){
!      printf("This is a file\n");

       return 0;
  }

- void 0;
--- 1,8 ----
  #include <stdio.h>

! function newd(){
!      printf("This is a new file\n");

       return 0;
  }

下面來看看怎麽對多個文件應用補丁,首先生成一個補丁文件,我們還是用上下文的格式。diff -c original update > directory.patch

在一個新的目錄下拷貝 original 文件夾和補丁文件,執行 patch -i directory.patch,此時會提示找不到文件,因為patch會在當前文件夾查找文件(默認情況下patch會將文件名前的所有文件夾去掉)因為此時補丁文件在文件夾外面,所以我們應當告訴patch不要這麽做,使用-p參數。

patch -p0 -i directory.patch

也許有人會問,如果我把補丁文件移動到文件夾中進行打補丁操作不就可以了嘛,註意千萬不要這麽做。如果文件夾中還有子文件夾,那麽patch不會到子文件夾中尋找文件,這樣就會對結果產生影響,特別是在不同文件夾中有相同名字的文件的時候。

還原補丁文件的操作

有時候版本需要進行回撤,這時可以使用 -R 參數。

patch -p0 -R -i directory.patch

Unified Format

GNU的diff和patch還提供了一種格式,稱為 the unified format。這個格式更加精簡,與上下文格式類似。但是不再將源文件和更新文件分開,而是組合在一起。並且沒有特殊的替換標誌,只有-+

diff -u original update

技術分享圖片

寫在最後

對文本文件進行patch操作時,提前備份是一個好習慣,這可以避免你在弄錯的情況下,面臨一堆無法恢復的文件發愁。

參考資料:
1、Using Diff and patch
2、Diff比較兩個文件夾
3、GNU Diff and patch

Linux中的Diff和Patch