diff コマンドで差分を確認するいろいろなオプション[Linux]

diff コマンドで差分を確認するいろいろなオプション[Linux]

diff コマンドの基本

diff – Wikipedia

diff コマンドは2つのファイルを行ごとに差分を確認するための Linux コマンドです。

2つのファイルパスを指定すれば差分がある行を表示できます。

$ diff [FILE1] [FILE2]

サンプルデータ

サンプルデータとして a.txtb.txt の2ファイルを用意します。

a.txt

aaXa
cccc
dddd
eeee
ffff
gggg
hhhh
iiii
jjjj
kkkk

b.txt

aaaa
bbbb
cccc
dddd
eeee
GGGG
hhhh
iiii
jjjj
kkkk

差分表示の方式

-u: ユニファイド形式 (Unified format)

ユニファイド形式 (Unified format) – Wikipedia

最も広く使われるdiffの表示形式です。同じ文脈で比較できるファイルの差分を表示します。

-u オプションを指定するとユニファイル形式で表示します。

$ diff a.txt b.txt -u
--- a.txt       2021-02-14 21:23:13.388019400 +0900
+++ b.txt       2021-02-14 21:07:26.211076200 +0900
@@ -1,9 +1,9 @@
-aaXa
+aaaa
+bbbb
 cccc
 dddd
 eeee
-ffff
-gggg
+GGGG
 hhhh
 iiii
 jjjj

@@ -1,9 +1,9 @@ となっている個所は、1行目から9行目までの範囲で変更箇所があるという意味です。その範囲での差分が直後のブロックで表示されています。

+ から始まる行は挿入行、- から始まる行は削除行です。 (スペース)から始まる行は変更なしの行です。

-U [行数] で差分のある行の後ろ何行を合わせて表示するかを指定できます。デフォルト(-u)だと3行になります。

試しに -U 0 とすると変更のあった差分が出た行だけが確認できます。ちょうど何行目に変更があったかが確認できます。

$ diff a.txt b.txt -U 0
--- a.txt       2021-02-14 21:23:13.388019400 +0900
+++ b.txt       2021-02-14 21:07:26.211076200 +0900
@@ -1 +1,2 @@
-aaXa
+aaaa
+bbbb
@@ -5,2 +6 @@
-ffff
-gggg
+GGGG

-y: 2カラム形式

2つのファイルを左右に並べて比較する形式です。-y オプションで指定します。

1ファイル目、最初に指定したファイルが左に表示され、2ファイル目が右に表示されます。

-W [全体の幅数] を併せて指定すれば見やすくなります。デフォルトだと130です。コンソールの表示が小さいと変な位置で改行されて見づらいかもしれません。

幅30の2列表示で差分を出力すると以下のようになります。

$ diff a.txt b.txt -y -W 30
aaXa          | aaaa
              > bbbb
cccc            cccc
dddd            dddd
eeee            eeee
ffff          | GGGG
gggg          <
hhhh            hhhh
iiii            iiii
jjjj            jjjj
kkkk            kkkk

右列に | となっている個所が変更がある行、> となっている行が2ファイル目に追加された行、< となっている行が1ファイル目に追加された行です。それ以外の行は差分なしの行です。

少ない行数だと見やすいですが、余り大きいサイズのファイルだと全行表示されて見づらいです。見やすくするためにいくつかのオプションが使えます。

--left-column オプションを指定すると差分がない行は左列にだけ表示できます。

$ diff a.txt b.txt -y -W 30 --left-column
aaXa          | aaaa
              > bbbb
cccc          (
dddd          (
eeee          (
ffff          | GGGG
gggg          <
hhhh          (
iiii          (
jjjj          (
kkkk          (

--suppress-common-lines オプションを指定すると、差分行のみ表示できます。

$ diff a.txt b.txt -y -W 30 --suppress-common-lines
aaXa          | aaaa
              > bbbb
ffff          | GGGG
gggg          <

わかりやすいですが、差分のあった行番号が表示できないので同じようなデータが続くファイルだとわかりづらいかもしれません。

<: コマンドの出力結果を使って diff をとる

何かしらのコマンドを実行してその標準出力の結果を使って差分をとることも可能です。リダイレクトを使って次のように記述します。

$ diff <(コマンド1) <(コマンド2)

カッコ()の中に好きなコマンドを入れて実行すればよいです。片方をファイル名にしてももちろんOKです。

例えば echo で出力した内容で差分をとってみます。

$ diff <(echo a) <(echo b) -u
--- /dev/fd/63  2021-02-14 22:47:13.382567000 +0900
+++ /dev/fd/62  2021-02-14 22:47:13.384406300 +0900
@@ -1 +1 @@
-a
+b

いい感じに確認できました。

以下、echo の結果を使って diff コマンドの例を見ていきます。

差分の判定いろいろ

-i: 大文字小文字の区別を無視する

-i(“) を指定すると大文字と小文字の違いを無視します。

# -i なし(大文字小文字を区別)
$ diff <(echo a) <(echo A) -u
--- /dev/fd/63  2021-02-14 22:48:28.416033600 +0900
+++ /dev/fd/62  2021-02-14 22:48:28.417033300 +0900
@@ -1 +1 @@
-a
+A

# -i あり(大文字小文字を区別しない)
$ diff <(echo a) <(echo A) -u -i

'a' と 'A' を区別しないようになっています。

-E: タブの展開を無視する

タブ文字を半角スペースに展開された結果を無視します。具体的にはタブ文字と半角スペース4つを同一として無視します。

$ diff -E <(echo -e "hoge\thoge") <(echo "hoge    hoge")

$ diff -E -u <(echo -e "hoge\thoge") <(echo "hoge  hoge")
--- /dev/fd/63  2021-02-14 22:57:54.473863600 +0900
+++ /dev/fd/62  2021-02-14 22:57:54.477864500 +0900
@@ -1 +1 @@
-hoge   hoge
+hoge  hoge

-Z: 末尾の改行を無視する

差分比較するときに末尾にスペースがあるかないかを無視して比較します。

$ diff -Z <(echo "hoge") <(echo "hoge     ")
# 末尾のスペースを無視するので差分なし

-b: スペースの個数を無視する

連続するスペースの個数を無視します。1つだけだろうが2つ以上続こうが同じひとまとまりのスペースとして扱います。

$ diff -b <(echo -e "hoge hoge") <(echo "hoge    hoge")
# スペースの個数を無視するので差分なし

-w: スペースをすべて無視する

比較対象の文字列からすべてのスペースを無視して比較します。

$ diff -w <(echo "h  o  g  e") <(echo "   hoge   ")
# スペースをすべて無視するので差分なし

-B: 空行を無視する

比較対象からすべての空行を無視して比較します。

$ diff -B <(echo -e "\nhoge\n\n\n") <(echo -e "hoge\n")
# すべての空行を無視するので差分なし

-r: 再帰的にディレクトリの中身の diff をとる

-r のオプションを付けてディレクトを差分をとる対象として指定すると、ディレクトリを再帰的に走査して差分を取得してくれます。

例えば以下のようなディレクトリ構成があったとして

.
├── a
│   ├── x.txt
│   ├── y.txt
│   └── z.txt
└── b
    ├── x.txt
    └── y.txt

a/x.txtb/x.txt の内容が異なるとします。以下のコマンドでディレクトリ a/b/ の差分を再帰的に取ってみます。

$ diff -r -u a/ b/
diff -r -u a/x.txt b/x.txt
--- a/x.txt     2021-02-15 00:03:28.708367900 +0900
+++ b/x.txt     2021-02-15 00:03:21.597556100 +0900
@@ -1 +1 @@
-x
+xxx
Only in a/: z.txt

こんな感じの出力でファイル内容の差分と、そもそもファイルが存在しない場合にその旨が出力されていることが確認できます。

その他オプションのいろいろ

差分確認のためのオプションがいろいろ用意されています。 diff --help の内容を見てください。

以上です。

$ diff --help
Usage: diff [OPTION]... FILES
Compare FILES line by line.

Mandatory arguments to long options are mandatory for short options too.
      --normal                  output a normal diff (the default)
  -q, --brief                   report only when files differ
  -s, --report-identical-files  report when two files are the same
  -c, -C NUM, --context[=NUM]   output NUM (default 3) lines of copied context
  -u, -U NUM, --unified[=NUM]   output NUM (default 3) lines of unified context
  -e, --ed                      output an ed script
  -n, --rcs                     output an RCS format diff
  -y, --side-by-side            output in two columns
  -W, --width=NUM               output at most NUM (default 130) print columns
      --left-column             output only the left column of common lines
      --suppress-common-lines   do not output common lines

  -p, --show-c-function         show which C function each change is in
  -F, --show-function-line=RE   show the most recent line matching RE
      --label LABEL             use LABEL instead of file name and timestamp
                                  (can be repeated)

  -t, --expand-tabs             expand tabs to spaces in output
  -T, --initial-tab             make tabs line up by prepending a tab
      --tabsize=NUM             tab stops every NUM (default 8) print columns
      --suppress-blank-empty    suppress space or tab before empty output lines
  -l, --paginate                pass output through 'pr' to paginate it

  -r, --recursive                 recursively compare any subdirectories found
      --no-dereference            don't follow symbolic links
  -N, --new-file                  treat absent files as empty
      --unidirectional-new-file   treat absent first files as empty
      --ignore-file-name-case     ignore case when comparing file names
      --no-ignore-file-name-case  consider case when comparing file names
  -x, --exclude=PAT               exclude files that match PAT
  -X, --exclude-from=FILE         exclude files that match any pattern in FILE
  -S, --starting-file=FILE        start with FILE when comparing directories
      --from-file=FILE1           compare FILE1 to all operands;
                                    FILE1 can be a directory
      --to-file=FILE2             compare all operands to FILE2;
                                    FILE2 can be a directory

  -i, --ignore-case               ignore case differences in file contents
  -E, --ignore-tab-expansion      ignore changes due to tab expansion
  -Z, --ignore-trailing-space     ignore white space at line end
  -b, --ignore-space-change       ignore changes in the amount of white space
  -w, --ignore-all-space          ignore all white space
  -B, --ignore-blank-lines        ignore changes where lines are all blank
  -I, --ignore-matching-lines=RE  ignore changes where all lines match RE

  -a, --text                      treat all files as text
      --strip-trailing-cr         strip trailing carriage return on input

  -D, --ifdef=NAME                output merged file with '#ifdef NAME' diffs
      --GTYPE-group-format=GFMT   format GTYPE input groups with GFMT
      --line-format=LFMT          format all input lines with LFMT
      --LTYPE-line-format=LFMT    format LTYPE input lines with LFMT
    These format options provide fine-grained control over the output
      of diff, generalizing -D/--ifdef.
    LTYPE is 'old', 'new', or 'unchanged'.  GTYPE is LTYPE or 'changed'.
    GFMT (only) may contain:
      %<  lines from FILE1
      %>  lines from FILE2
      %=  lines common to FILE1 and FILE2
      %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER
        LETTERs are as follows for new group, lower case for old group:
          F  first line number
          L  last line number
          N  number of lines = L-F+1
          E  F-1
          M  L+1
      %(A=B?T:E)  if A equals B then T else E
    LFMT (only) may contain:
      %L  contents of line
      %l  contents of line, excluding any trailing newline
      %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number
    Both GFMT and LFMT may contain:
      %%  %
      %c'C'  the single character C
      %c'\OOO'  the character with octal code OOO
      C    the character C (other characters represent themselves)

  -d, --minimal            try hard to find a smaller set of changes
      --horizon-lines=NUM  keep NUM lines of the common prefix and suffix
      --speed-large-files  assume large files and many scattered small changes
      --color[=WHEN]       colorize the output; WHEN can be 'never', 'always',
                             or 'auto' (the default)
      --palette=PALETTE    the colors to use when --color is active; PALETTE is
                             a colon-separated list of terminfo capabilities

      --help               display this help and exit
  -v, --version            output version information and exit

FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'.
If --from-file or --to-file is given, there are no restrictions on FILE(s).
If a FILE is '-', read standard input.
Exit status is 0 if inputs are the same, 1 if different, 2 if trouble.

Report bugs to: bug-diffutils@gnu.org
GNU diffutils home page: <http://www.gnu.org/software/diffutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>

参考URL

Linuxカテゴリの最新記事