patch 程式

  • 為什麼 patch?
    1. 檔案( 版本 )之間的差異,可以指令 diff 儲存在一個 patch 檔案。
    2. 若舊版本需要修改,只要將 patch 檔案釋出。
    3. 使用者可以指令 patch,配合 patch 檔案中記錄之新舊版差異,將舊版程式更新。
    4. 使用者若發現並修正一個程式的臭蟲,簡單、正確的方式是,寄一個 patch 檔案給作者,而不要只是說明修正的地方。
  • diff 指令:比對兩個檔案之間的差異,一般是用在 ASCII 純文字檔的比對上。
    1. diff 指令用法: 

      [root@linux ~]# diff [-bBiqn] from-file to-file
      選項:
      from-file :檔名,作為原始比對檔案的檔名;
      to-file   :檔名,作為目的比對檔案的檔名;
      # from-file 或 to-file 可以 - 取代, - 代表『Standard input』。
      
      -b :忽略一行當中,多個空白的差異
           (例如 "about me" 與 "about     me" 視為相同)
      -B :忽略空白行的差異。
      -i :忽略大小寫的不同。
      -q :只列出檔案是否有差異。
      -n :以 RCS 格式輸出檔案之差異。
      -c (-C NUM) :兩個檔案皆加入差異部分前後 NUM 行,以增加輸出之可讀性。預設 NUM=3。 
      -u (-U NUM) :加入差異部分前後 NUM 行,以增加輸出之可讀性。預設 NUM=3。
      

    2. 預處理:將 /etc/passwd 第四行刪除,第六行取代為『no six line』,新的檔案放到 /tmp/test。 

      [root@linux ~]# mkdir -p /tmp/test
      [root@linux ~]# cat /etc/passwd | \
      > sed -e '4d' -e '6c no six line' > /tmp/test/passwd
      # sed 後面若要接超過兩個以上的動作時,每個動作前面得加 -e 。
      

    3. 比對 /tmp/test/passwd 與 /etc/passwd 的差異。 

      [root@dywHome2 ~]# diff /etc/passwd /tmp/test/passwd
      4d3
      < adm:x:3:4:adm:/var/adm:/bin/sh
      6c5
      < sync:x:5:0:sync:/sbin:/bin/sync
      ---
      > no six line
      

    4. 只列出檔案是否有差異。 

      [root@dywHome2 ~]# diff -q /etc/passwd /tmp/test/passwd
      

    5. 以 RCS 格式輸出檔案之差異。 

      [root@dywHome2 ~]# diff -n /etc/passwd /tmp/test/passwd
      d4 1
      d6 1
      a6 1
      no six line
      

    6. 加入差異部分前後 3 行,以增加輸出之可讀性。 

      [root@dywHome2 tmp]# diff -u old/ new/ > test.patch
      [root@dywHome2 tmp]# cat test.patch
      diff -u old/passwd new/passwd
      --- old/passwd  2008-04-16 13:14:50.000000000 -0400
      +++ new/passwd  2008-04-16 11:15:12.000000000 -0400
      @@ -1,9 +1,8 @@
       root:x:0:0:root:/root:/bin/bash
       bin:x:1:1:bin:/bin:/bin/sh
       daemon:x:2:2:daemon:/sbin:/bin/sh
      -adm:x:3:4:adm:/var/adm:/bin/sh
       lp:x:4:7:lp:/var/spool/lpd:/bin/sh
      -sync:x:5:0:sync:/sbin:/bin/sync
      +no six line
       shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
       halt:x:7:0:halt:/sbin:/sbin/halt
       mail:x:8:12:mail:/var/spool/mail:/bin/sh
      

    7. 比對 /etc 與 /tmp/test 的差異。 

      [root@linux ~]# diff /etc /tmp/test
      ......(前面省略).....
      Only in /etc: paper.config
      diff /etc/passwd /tmp/test/passwd
      4d3
      < adm:x:3:4:adm:/var/adm:/sbin/nologin
      6c5
      < sync:x:5:0:sync:/sbin:/bin/sync
      ---
      > no six line
      Only in /etc: passwd-
      ......(後面省略).....
      

  • cmp:主要利用『位元』單位去比對(diff 主要是以『行』為單位比對,cmp 則是以『位元』為單位去比對)。
    1. cmp 指令用法: 

      [root@linux ~]# cmp [-bcsi] file1 file2
      選項:
      -b :列出第一個的不同點之字元。
      -c :同上。
      -i SKIP1:SKIP2 : file1 與 file2 分別忽略前 SKIP1 與 SKIP2 位元。
      -s :安靜模式,不顯示任何訊息。
      

    2. 用 cmp 比較 /etc/passwd 與 /tmp/test/passwd 

      [root@dywHome2 ~]# cmp /etc/passwd /tmp/test/passwd
      /etc/passwd /tmp/test/passwd differ: byte 94, line 4
      # 不同點在第四行,而且位元數是在第 94 個位元。
      

    3. 列出第一個的不同點之字元 

      [root@dywHome2 ~]# cmp -c /etc/passwd /tmp/test/passwd
      /etc/passwd /tmp/test/passwd differ: byte 94, line 4 is 141 a 154 l
      # 不同點在檔案 /etc/passwd 的第一個字元為 a,在檔案 /tmp/test/passwd 的第一個字元為 l。
      # 字元 a 之八進位碼為 141,字元 l 之八進位碼為 154。
      

    4. 以 printf 驗證 ASCII 之字元 

      [root@dywHome2 ~]# printf '\x61\t\x6c\n'
      a       l
      # \xNN : NN 為十六進位
      

  • patch:檔案補丁。需與 diff 配合使用。
    1. patch 指令用法 

      [root@linux ~]# patch [OPTION]... [ORIGFILE [PATCHFILE]]
      選項:
      -pNUM :取消 NUM 層目錄。
       例如:假設檔名 /u/howard/src/blurfl/blurfl.c
            -p0 :代表  u/howard/src/blurfl/blurfl.c
            -p4 :代表  blurfl/blurfl.c
      -l     :忽略空白之差異。
      -i PATCHFILE :從 PATCHFILE 讀取補丁。
      -o FILE :輸出補丁到檔案 FILE。
      -r FILE :輸出錯誤到檔案 FILE。
      

    2. 預處理:建立兩個不同版本的檔案 /tmp/test/passwd 與 /etc/passwd。 

      [root@dywHome2 ~]# mkdir /tmp/old; cp /etc/passwd /tmp/old
      [root@dywHome2 ~]# mkdir /tmp/new; cp /tmp/test/passwd /tmp/new
      

    3. 建立補丁檔案 /tmp/test.patch 記錄新舊檔案之間的差異。 

      [root@dywHome2 ~]# cd /tmp ; diff old/ new/ > test.patch
      # diff 製作檔案時,舊的檔案必須是在前面,亦即是 diff oldfile newfile。
      

    4. 將舊的內容 (/tmp/old/passwd) 更新到新版 (/tmp/new/passwd) 的內容 

      [root@dywHome2 tmp]# cd /tmp/old
      [root@dywHome2 old]# patch passwd -i /tmp/test.patch
      patching file passwd
      # 選項 -i 亦可省略
      

    5. 更新內容,並指定存於 passwd1 

      [root@dywHome2 old]# patch passwd /tmp/test.patch -o passwd1
      patching file passwd
      

    6. 內容已更新,若再做一次補丁,系統會詢問是否執行?
      1. 預設回答 n(o):系統會將錯誤訊息存在 passwd.rej 

        [root@dywHome2 old]# patch passwd -i /tmp/test.patch
        patching file passwd
        Reversed (or previously applied) patch detected!  Assume -R? [n] n
        Apply anyway? [n] n
        Skipping patch.
        2 out of 2 hunks ignored -- saving rejects to file passwd.rej
        

      2. 回答 y(es):系統會將更新後的內容存在 passwd.orig 

        [root@dywHome2 old]# patch passwd -i /tmp/test.patch
        patching file passwd
        Reversed (or previously applied) patch detected!  Assume -R? [n] y
        

      3. 查詢檔案 

        [root@dywHome2 old]# ll passwd*
        -rw-r--r-- 1 root root 1207 Apr 16 13:14 passwd
        -rw------- 1 root root 1156 Apr 16 13:10 passwd1
        -rw-r--r-- 1 root root 1156 Apr 16 13:11 passwd.orig
        -rw-r--r-- 1 root root  149 Apr 16 13:12 passwd.rej
        

    7. 使用選項 -pNUM 更新舊版資料
      1. 變換目錄至 /tmp 

        [root@dywHome2 old]# cd ..
        

      2. 以選項 -u 建立 目錄 old/ 與 new/ 下檔案之差異檔,再回到目錄 /tmp/old。 

        [root@dywHome2 tmp]# diff -u old/ new/ > /tmp/test.patch
        [root@dywHome2 tmp]# cd old
        

      3. test.patch 儲存 diff -u old/passwd new/passwd,故必須一層目錄,即 -p1。 

        [root@dywHome2 old]# patch -p1 </tmp/test.patch
        patching file passwd
        

      4. 若使用 -p0,則無法找到要更新的檔案,系統會要求輸入要更新的檔案。 

        [root@dywHome2 old]# patch -p0 </tmp/test.patch
        can't find file to patch at input line 4
        Perhaps you used the wrong -p or --strip option?
        The text leading up to this was:
        --------------------------
        |diff -u old/passwd new/passwd
        |--- old/passwd 2008-04-16 14:46:21.000000000 -0400
        |+++ new/passwd 2008-04-16 11:15:12.000000000 -0400
        --------------------------
        File to patch:
        

      5. 命令 patch -pnum <patchfile> 適用於目錄下多個檔案,較接近實際狀況。
  • 範例:利用 patch 命令及第一版檔案、第一版和第二版的差異檔案,產生完整的第二版檔案。
    1. 第一版的檔案 file1.c: 

      This is file one
      line 2
      line 3
      there is no line 4, this is line 5
      line 6
      

    2. 產生第二版的檔案 file2.c: 

      This is file two
      line 2
      line 3
      line 4
      line 5
      line 6
      a new line 8
      

    3. 利用 diff 命令產生差異: 

      $ diff file1.c file2.c > diffs
      diffs 檔案如下:
      1c1
      < This is file one
      —
      > This is file two
      4c4,5
      < there is no line 4, this is line 5
      —
      > line 4
      > line 5
      5a7
      > a new line 8
      

    4. 假設有 file1.c 和 diffs 檔案。可以利用 patch 命令更新 file1.c,使其與 file2.c 一樣。 

      $ patch file1.c diffs
      Hmm... Looks like a normal diff to me...
      Patching file file1.c using Plan A...
      Hunk #1 succeeded at 1.
      Hunk #2 succeeded at 4.
      Hunk #3 succeeded at 7.
      done
      $
      

    5. 若希望回覆 file1.c ,只要再使用 patch,加上-R(反向 patch)選項,file1.c 就會回到原本的狀況。。 

      $ patch -R file1.c diffs
      Hmm... Looks like a normal diff to me...
      Patching file file1.c using Plan A...
      Hunk #1 succeeded at 1.
      Hunk #2 succeeded at 4.
      Hunk #3 succeeded at 6.
      done
      $
      

  • 例題:完成下列工作。
    1. 以 vi 在 printf.txt 最後加入一行 csie - - -,並另存為 printf.new;
    2. 以 diff 比較 printf.txt 及 printf.new,並建立其 patch file,printf.patch;
    3. 以 cmp 比較 printf.txt 及 printf.new;
    4. 利用 patch file,printf.patch 將 printf.txt 更新為 printf.new。

猜你喜欢

转载自huaonline.iteye.com/blog/1756440