2つのファイルを比較する方法


回答:


92

diffコマンドを調べてください。これは優れたツールでありman diff、ターミナルに入力することですべてを読むことができ ます。

実行するコマンドdiff File_1.txt File_2.txtは、2つの間の差を出力し、次のようになります。

ここに画像の説明を入力してください

3番目のコマンドからの出力の読み取りに関する簡単な注意:「矢印」(<および>)は、左側のファイル(<)と右側のファイル(>)の行の値を示し、左側のファイルは入力したものですこの場合、最初にコマンドラインでFile_1.txt

さらに、4番目のコマンドがdiff ... | tee Output_Fileから結果diffをaにパイプし、teeその出力をファイルに書き込むことに気付くかもしれません。その出力をすぐにコンソールに表示したくない場合は、後で保存できるようになります。


これで他のファイル(画像など)を実行できますか?それとも、ドキュメントだけに制限されていますか?
グレゴリーオペラ14

2
私の知る限り、テキストファイルに限定されています。コードは基本的にテキストであるため機能しますが、バイナリファイル(画像)は単にジャンクアウトされます。以下を実行することにより、それらが同一であるかどうかを比較できますdiff file1 file2 -s。次に例を示します。imgur.com
ミッチ

出力を色付けする方法はありますか?CLIのみを使用したいのですが、さらに...人間的なタッチが必要です。
ラザールリュベノビッチ

36

または、Meld Diffを使用できます

Meldは、ファイル、ディレクトリ、およびバージョン管理されたプロジェクトを比較するのに役立ちます。ファイルとディレクトリの両方の2者間および3者間比較を提供し、多くの一般的なバージョン管理システムをサポートしています。

次を実行してインストールします。

sudo apt-get install meld

あなたの例:

ここに画像の説明を入力してください

ディレクトリを比較:

ここに画像の説明を入力してください

テキストの完全な例:

ここに画像の説明を入力してください


18

vimdiffを使用できます。

例:

vimdiff  file1  file2

1
これには色があります
ジェイクトロント

これは、最初のファイルの行末がでdos、2番目の行末がであることが示されたので役立ちましたunix
LoMaPh

13

FWIW、私はdiffからの並列出力で得られるものが好きです

diff -y -W 120 File_1.txt File_2.txt

次のようなものを与えるでしょう:

User1 US                            User1 US
User2 US                            User2 US
User3 US                          | User3 NG


8

Meld本当に素晴らしいツールです。ただし、次のdiffuse2つのファイルを視覚的に比較することもできます。

diffuse file1.txt file2.txt

ここに画像の説明を入力してください


7

質問(file1、file2、「has changed」メッセージを含むoutputfile)にこだわる以下のスクリプトが機能します。

スクリプトを空のファイルにコピーし、名前を付けて保存しcompare.py、実行可能にし、コマンドで実行します。

/path/to/compare.py <file1> <file2> <outputfile>

スクリプト:

#!/usr/bin/env python

import sys
file1 = sys.argv[1]; file2 = sys.argv[2]; outfile = sys.argv[3]

def readfile(file):
    with open(file) as compare:
        return [item.replace("\n", "").split(" ") for item in compare.readlines()]

data1 = readfile(file1); data2 = readfile(file2)
mismatch = [item[0] for item in data1 if not item in data2]

with open(outfile, "wt") as out:
    for line in mismatch:
        out.write(line+" has changed"+"\n")

数行追加することで、出力ファイルが定義されているかどうかに応じて、出力ファイルまたは端末に出力できます。

ファイルに印刷するには:

/path/to/compare.py <file1> <file2> <outputfile>

端末ウィンドウに印刷するには:

/path/to/compare.py <file1> <file2> 

スクリプト:

#!/usr/bin/env python

import sys

file1 = sys.argv[1]; file2 = sys.argv[2]
try:
    outfile = sys.argv[3]
except IndexError:
    outfile = None

def readfile(file):
    with open(file) as compare:
        return [item.replace("\n", "").split(" ") for item in compare.readlines()]

data1 = readfile(file1); data2 = readfile(file2)
mismatch = [item[0] for item in data1 if not item in data2]

if outfile != None:
        with open(outfile, "wt") as out:
            for line in mismatch:
                out.write(line+" has changed"+"\n")
else:
    for line in mismatch:
        print line+" has changed"

4

簡単な方法は、を使用することですcolordiff。これはdiff、出力のように動作しますが、出力を色付けします。これは差分を読むのに非常に役立ちます。あなたの例を使用して、

$ colordiff -u File_1.txt File_2.txt
--- File_1.txt  2016-12-24 17:59:17.409490554 -0500
+++ File_2.txt  2016-12-24 18:00:06.666719659 -0500
@@ -1,3 +1,3 @@
 User1 US
 User2 US
-User3 US
+User3 NG

uオプションは統一された差分を提供します。これは、色付けされた差分がどのように見えるかです:

ここに画像の説明を入力してください

colordiff実行してインストールしますsudo apt-get install colordiff


1
あなたは色をしたい場合、私はvimのに組み込まれたdiffが実際にMr.Sによって答えのように、使いやすいように見つける
thomasrutter

2

追加の回答

ファイルのどの部分が異なるかを知る必要がない場合は、ファイルのチェックサムを使用できます。md5sumまたはを使用して、これを行う方法は多数ありますsha256sum。基本的に、それらはそれぞれファイル内容がハッシュする文字列を出力します。2つのファイルが同じ場合、それらのハッシュも同じになります。これは、Ubuntuインストールisoイメージなどのソフトウェアをダウンロードするときによく使用されます。多くの場合、ダウンロードされたコンテンツの整合性を検証するために使用されます。

以下のスクリプトを検討してください。ここでは、2つのファイルを引数として指定できます。ファイルは、それらが同じかどうかを示します。

#!/bin/bash

# Check if both files exist  
if ! [ -e "$1"  ];
then
    printf "%s doesn't exist\n" "$1"
    exit 2
elif ! [ -e "$2" ]
then
    printf "%s doesn't exist\n" "$2"
    exit 2
fi

# Get checksums of eithe file
file1_sha=$( sha256sum "$1" | awk '{print $1}')
file2_sha=$( sha256sum "$2" | awk '{print $1}')

# Compare the checksums
if [ "x$file1_sha" = "x$file2_sha" ]
then
    printf "Files %s and %s are the same\n" "$1" "$2"
    exit 0
else
    printf "Files %s and %s are different\n" "$1" "$2"
    exit 1
fi

サンプル実行:

$ ./compare_files.sh /etc/passwd ./passwd_copy.txt                                                                
Files /etc/passwd and ./passwd_copy.txt are the same
$ echo $?
0
$ ./compare_files.sh /etc/passwd /etc/default/grub                                                                
Files /etc/passwd and /etc/default/grub are different
$ echo $?
1

古い回答

さらにcomm、2つのソートされたファイルを比較し、3列に出力するコマンドがあります。列1はファイル#1に固有のアイテム、列2はファイル#2に固有のアイテム、列3は両方のファイルに存在します。

いずれかの列を抑制するには、スイッチ-1、-2、および-3を使用できます。-3を使用すると、異なる行が表示されます。

以下に、実行中のコマンドのスクリーンショットを見ることができます。

ここに画像の説明を入力してください

要件は1つだけです。ファイルを適切に比較するには、ファイルを並べ替える必要があります。sortコマンドはその目的に使用できます。以下は、ファイルを並べ替えて比較する別のスクリーンショットです。File_1への左側の先頭から始まる行のみ、列2から始まる行はFile_2のみに属します

ここに画像の説明を入力してください


@DavidFoersterモバイルで編集するのはちょっと難しい:)しかし、今
やる

2

gitをインストールして使用する

$ git diff filename1 filename2

そして、素敵な色のフォーマットで出力が得られます

Gitのインストール

$ apt-get update
$ apt-get install git-core

2

colcmp.sh

形式の2つのファイルの名前/値のペアを比較しますname value\n。変更さnameれたOutput_file場合にtoを書き込みます。連想配列には bash v4 +が必要です。

使用法

$ ./colcmp.sh File_1.txt File_2.txt
User3 changed from 'US' to 'NG'
no change: User1,User2

Output_File

$ cat Output_File
User3 has changed

ソース(colcmp.sh)

cmp -s "$1" "$2"
case "$?" in
    0)
        echo "" > Output_File
        echo "files are identical"
        ;;
    1)
        echo "" > Output_File
        cp "$1" ~/.colcmp.array1.tmp.sh
        sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
        sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array1.tmp.sh
        sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.array1.tmp.sh
        chmod 755 ~/.colcmp.array1.tmp.sh
        declare -A A1
        source ~/.colcmp.array1.tmp.sh

        cp "$2" ~/.colcmp.array2.tmp.sh
        sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array2.tmp.sh
        sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array2.tmp.sh
        sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A2\\[\\1\\]=\"\\2\"/" ~/.colcmp.array2.tmp.sh
        chmod 755 ~/.colcmp.array2.tmp.sh
        declare -A A2
        source ~/.colcmp.array2.tmp.sh

        USERSWHODIDNOTCHANGE=
        for i in "${!A1[@]}"; do
            if [ "${A2[$i]+x}" = "" ]; then
                echo "$i was removed"
                echo "$i has changed" > Output_File
            fi
        done
        for i in "${!A2[@]}"; do
            if [ "${A1[$i]+x}" = "" ]; then
                echo "$i was added as '${A2[$i]}'"
                echo "$i has changed" > Output_File
            elif [ "${A1[$i]}" != "${A2[$i]}" ]; then
                echo "$i changed from '${A1[$i]}' to '${A2[$i]}'"
                echo "$i has changed" > Output_File
            else
                if [ x$USERSWHODIDNOTCHANGE != x ]; then
                    USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
                fi
                USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"
            fi
        done
        if [ x$USERSWHODIDNOTCHANGE != x ]; then
            echo "no change: $USERSWHODIDNOTCHANGE"
        fi
        ;;
    *)
        echo "error: file not found, access denied, etc..."
        echo "usage: ./colcmp.sh File_1.txt File_2.txt"
        ;;
esac

説明

コードの内訳とその意味を、できる限り理解してください。編集や提案を歓迎します。

基本的なファイル比較

cmp -s "$1" "$2"
case "$?" in
    0)
        # match
        ;;
    1)
        # compare
        ;;
    *)
        # error
        ;;
esac

cmp $?の値を設定します 以下

  • 0 =ファイルが一致
  • 1 =ファイルが異なる
  • 2 =エラー

私が使用することを選択した場合 ... ESACの evaluteする文を$?なぜなら$の値はテスト([)を含むすべてのコマンドの後に変更されます。

または、変数を使用して$?の値を保持することもできます。

cmp -s "$1" "$2"
CMPRESULT=$?
if [ $CMPRESULT -eq 0 ]; then
    # match
elif [ $CMPRESULT -eq 1 ]; then
    # compare
else
    # error
fi

上記は、caseステートメントと同じことを行います。私が好きなIDK。

出力をクリアする

        echo "" > Output_File

上記は出力ファイルをクリアするため、ユーザーが変更されていない場合、出力ファイルは空になります。

エラー時にOutput_fileが変更されないように、caseステートメント内でこれを行います。

ユーザーファイルをシェルスクリプトにコピーする

        cp "$1" ~/.colcmp.arrays.tmp.sh

上記はFile_1.txtを現在のユーザーのホームディレクトリにコピーします。

たとえば、現在のユーザーがjohnの場合、上記はcp "File_1.txt" /home/john/.colcmp.arrays.tmp.shと同じになります。

特殊文字をエスケープする

基本的に、私は妄想です。これらの文字は、変数の割り当ての一部としてスクリプトで実行されると、特別な意味を持つか、外部プログラムを実行できることを知っています。

  • `-バックティック-出力がスクリプトの一部であるかのようにプログラムと出力を実行します
  • $-ドル記号-通常、変数のプレフィックス
  • $ {}-より複雑な変数置換が可能
  • $()-これは何をするのかidkですが、コードを実行できると思います

が知らないのは、bashについてどれだけ知らないかです。他の文字が特別な意味を持っているかどうかはわかりませんが、バックスラッシュですべてをエスケープします。

        sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh

sedは、正規表現のパターンマッチングよりも多くのことができます。スクリプトパターン「s /(find)/(replace)/」は、特にパターンマッチを実行します。

「s /(find)/(replace)/(modifiers)」

英語:句読点または特殊文字をキャプチャグループ1としてキャプチャします(\\ 1)

英語:すべての特殊文字の前にバックスラッシュを付けます

  • (修飾子)= g
    • g =グローバルに置換

英語:同じ行に複数の一致が見つかった場合、それらをすべて置き換えます

スクリプト全体をコメントアウトする

        sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh

上記では、正規表現を使用して〜/ .colcmp.arrays.tmp.shのすべての行の先頭にbashコメント文字()を付けています。これは、後でsourceコマンドを使用して〜/ .colcmp.arrays.tmp.shを実行する予定であり、File_1.txtの形式全体が不明なためです。

誤って任意のコードを実行したくない。誰もそうは思わない。

「s /(検索)/(置換)/」

英語:各行をキャプチャグループ1としてキャプチャします(\\ 1)

  • (置換)=#\\ 1

英語:各行をポンド記号で置き換え、その後に置き換えられた行を置きます

ユーザー値をA1 [User] = "value"に変換します

        sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.sh

上記はこのスクリプトの中核です。

  • これを変換する: #User1 US
    • これに: A1[User1]="US"
    • またはこれ:A2[User1]="US"(2番目のファイルの場合)

「s /(検索)/(置換)/」

英語で:

  • 先頭のコメント文字(#)を必要とするが無視する
  • 先頭の空白を無視します
  • 最初の単語をキャプチャグループ1としてキャプチャします(\\ 1)
  • スペース(またはタブ、または空白)が必要です
    • 等号に置き換えられます
    • キャプチャグループの一部ではなく、
    • (置換)パターンは、キャプチャグループ1とキャプチャグループ2の間に等号を置きます
  • 残りの行をキャプチャグループ2としてキャプチャします

  • (置換)= A1 \\ [\\ 1 \\] = \ "\\ 2 \"

    • A1 \\ [- A1[と呼ばれる配列で配列の割り当てを開始するリテラル文字A1
    • \\ 1 = キャプチャグループ 1-先頭のハッシュ(#)を含まず、先頭の空白を含まない-この場合、キャプチャグループ1を使用して、bash連想配列の名前/値ペアの名前を設定しています。
    • \\] = \ "=リテラル文字 ]="
      • ]=配列の割り当てを閉じる(例:A1[User1 ]="US)"
      • = =代入演算子、たとえばvariable = value
      • " =空白をキャプチャするための値を引用する...考えてみると、上記のコードですべてをバックスラッシュしてスペース文字もバックスラッシュする方が簡単だったはずです。
    • \\ 1 = キャプチャグループ 2-この場合、名前/値のペアの値
    • "=スペースをキャプチャするための引用符の値を閉じる

英語:形式の各行を形式#name valueの配列代入演算子に置き換えますA1[name]="value"

実行可能にする

        chmod 755 ~/.colcmp.arrays.tmp.sh

上記では、chmodを使用して、アレイスクリプトファイルを実行可能にします。

これが必要かどうかはわかりません。

連想配列の宣言(bash v4 +)

        declare -A A1

大文字の-Aは、宣言された変数が連想配列になることを示します

これが、スクリプトにbash v4以降が必要な理由です。

配列変数割り当てスクリプトを実行する

        source ~/.colcmp.arrays.tmp.sh

我々はすでに持っています:

  • ラインから私たちのファイルを変換するUser valueのラインにA1[User]="value"
  • 実行可能にした(たぶん)
  • A1を連想配列として宣言しました...

上記では、現在のシェルでスクリプトを実行するためのソースを用意しています。これにより、スクリプトによって設定される変数値を保持できます。スクリプトを直接実行すると、新しいシェルが生成され、新しいシェルが終了すると変数値が失われます。少なくとも、それは私の理解です。

これは関数でなければなりません

        cp "$2" ~/.colcmp.array2.tmp.sh
        sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array2.tmp.sh
        sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.array2.tmp.sh
        sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A2\\[\\1\\]=\"\\2\"/" ~/.colcmp.array2.tmp.sh
        chmod 755 ~/.colcmp.array2.tmp.sh
        declare -A A2
        source ~/.colcmp.array2.tmp.sh

$ 1A1に対しても、$ 2A2に対して行うのと同じことを行います。それは本当に関数でなければなりません。この時点で、このスクリプトは十分に混乱し、機能していると思うので、修正するつもりはありません。

削除されたユーザーの検出

        for i in "${!A1[@]}"; do
            # check for users removed
        done

上記の連想配列キーのループ

            if [ "${A2[$i]+x}" = "" ]; then

上記では、変数置換を使用して、未設定の値と長さゼロの文字列に明示的に設定された変数の違いを検出します。

どうやら、変数が設定されているかどうか確認する方法はたくさんあります。投票数が最も多いものを選びました。

                echo "$i has changed" > Output_File

上記はユーザー$ iOutput_Fileに追加します

追加または変更されたユーザーの検出

        USERSWHODIDNOTCHANGE=

上記は変数をクリアするため、変更されなかったユーザーを追跡できます。

        for i in "${!A2[@]}"; do
            # detect users added, changed and not changed
        done

上記の連想配列キーのループ

            if ! [ "${A1[$i]+x}" != "" ]; then

上記は変数置換を使用して、変数が設定されているかどうか確認します

                echo "$i was added as '${A2[$i]}'"

ので、$ iの配列のキー(ユーザー名)$ A2 [$ i]がから現在のユーザーに関連付けられている値を返す必要がありますですFile_2.txtを

たとえば、$ iUser1の場合、上記は$ {A2 [User1]}として読み取ります

                echo "$i has changed" > Output_File

上記はユーザー$ iOutput_Fileに追加します

            elif [ "${A1[$i]}" != "${A2[$i]}" ]; then

ので、$ iの配列のキー(ユーザー名)$ A1 [$ i]がから現在のユーザーに関連付けられている値を返す必要がありますですFile_1.txtを、そして$ A2 [$ i]はから値を返す必要がありFile_2.txt

上記は、両方のファイルのユーザー$ iに関連付けられた値を比較しています。

                echo "$i has changed" > Output_File

上記はユーザー$ iOutput_Fileに追加します

                if [ x$USERSWHODIDNOTCHANGE != x ]; then
                    USERSWHODIDNOTCHANGE=",$USERSWHODIDNOTCHANGE"
                fi
                USERSWHODIDNOTCHANGE="$i$USERSWHODIDNOTCHANGE"

上記は、変更しなかったユーザーのコンマ区切りリストを作成します。リストにはスペースがないことに注意してください。スペースがない場合、次のチェックを引用符で囲む必要があります。

        if [ x$USERSWHODIDNOTCHANGE != x ]; then
            echo "no change: $USERSWHODIDNOTCHANGE"
        fi

上記の値が報告$ USERSWHODIDNOTCHANGEをが、価値がある場合のみ$ USERSWHODIDNOTCHANGE。これを記述する方法、$ USERSWHODIDNOTCHANGEにスペースを含めることはできません。スペースが必要な場合は、上記を次のように書き換えることができます。

        if [ "$USERSWHODIDNOTCHANGE" != "" ]; then
            echo "no change: $USERSWHODIDNOTCHANGE"
        fi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.