두 파일을 비교하는 방법


83

그래서 기본적으로 내가하고 싶은 것은 두 파일을 열 2별로 비교하는 것입니다. 어떻게 이것을 할 수 있습니까?

File_1.txt :

User1 US
User2 US
User3 US

File_2.txt :

User1 US
User2 US
User3 NG

결과물 파일:

User3 has changed

11
사용diff "File_1.txt" "File_2.txt"
Pandya

또한 방문 : askubuntu.com/q/12473
Pandya

답변:


92

diff명령을 살펴보십시오 . 좋은 도구이며 man diff터미널 에 입력하여 모든 정보를 읽을 수 있습니다 .

당신이하고 싶은 명령 diff File_1.txt File_2.txt은 둘 사이의 차이를 출력하고 다음과 같이 보일 것입니다 :

여기에 이미지 설명을 입력하십시오

세 번째 명령의 출력을 읽는 것에 대한 간단한 참고 사항 : '화살표'( <>)는 왼쪽 파일 ( <) 대 오른쪽 파일 ( >) 에있는 줄 값을 나타 냅니다. 왼쪽 파일은 입력 한 값입니다. 이 경우에는 명령 행에서 먼저File_1.txt

또한 네 번째 명령은 diff ... | tee Output_File결과를에서 diff로 파이프하여 tee출력을 파일로 저장하므로 나중에 콘솔에서 모든 것을 보지 않으려면 나중에 저장할 수 있습니다.


다른 파일 (예 : 이미지)을 수행 할 수 있습니까? 아니면 문서에만 국한됩니까?
Gregory Opera

2
내가 아는 한 텍스트 파일로 제한됩니다. 코드는 본질적으로 텍스트이므로 작동하지만 모든 이진 파일 (그림)은 정크를 제거합니다. 다음을 수행하여 동일한 지 여부를 비교할 수 있습니다 diff file1 file2 -s.. 예를 들면 다음과 같습니다. imgur.com/ShrQx9x
Mitch

출력물을 채색하는 방법이 있습니까? CLI 전용으로 유지하고 싶지만 좀 더 인간적인 손길이 필요합니다.
Lazar Ljubenović

36

또는 Meld Diff를 사용할 수 있습니다

Meld는 파일, 디렉토리 및 버전 제어 프로젝트를 비교하는 데 도움이됩니다. 파일과 디렉토리의 양방향 및 3 방향 비교를 제공하며 많은 인기있는 버전 제어 시스템을 지원합니다.

다음을 실행하여 설치하십시오.

sudo apt-get install meld

귀하의 예 :

여기에 이미지 설명을 입력하십시오

디렉토리 비교 :

여기에 이미지 설명을 입력하십시오

텍스트가 가득한 예 :

여기에 이미지 설명을 입력하십시오


18

vimdiff 를 사용할 수 있습니다 .

예:

vimdiff  file1  file2

1
이것은 색이있다
Jake Toronto

첫 번째 파일의 줄 끝이에 dos있고 두 번째 파일이에 있음을 보여 주면서 도움이되었습니다 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

10

다음 명령을 사용할 수 있습니다 cmp.

cmp -b "File_1.txt" "File_2.txt"

출력은

a b differ: byte 25, line 3 is 125 U 116 N

cmpdiff리턴 코드가 원하는 것보다 훨씬 빠릅니다 .
stevesliva

8

Meld정말 훌륭한 도구입니다. 그러나 diffuse두 파일을 시각적으로 비교 하는 데 사용할 수도 있습니다 .

diffuse file1.txt file2.txt

여기에 이미지 설명을 입력하십시오


7

Litteraly가 질문 (file1, file2, "변경된"메시지가있는 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입니다. 이것은 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옵션은 통합 된 diff를 제공합니다. 이것이 색상 화 된 diff의 모습입니다 :

여기에 이미지 설명을 입력하십시오

colordiff를 실행하여 설치하십시오 sudo apt-get install colordiff.


1
색상을 원한다면 Mr.S
thomasrutter

2

추가 답변

파일의 어떤 부분이 다른지 알 필요가없는 경우 파일의 체크섬을 사용할 수 있습니다. 사용하여 해당 작업을 수행하는 여러 가지 방법있다 md5sum거나 sha256sum. 기본적으로 각각은 파일 내용이 해시되는 문자열을 출력합니다. 두 파일이 동일하면 해시도 동일합니다. 우분투 설치 ISO 이미지와 같은 소프트웨어를 다운로드 할 때 자주 사용됩니다. 다운로드 한 콘텐츠의 무결성을 확인하는 데 자주 사용됩니다.

두 개의 파일을 인수로 제공 할 수있는 스크립트를 아래에서 고려하십시오. 그러면 파일이 동일한 지 여부를 알려줍니다.

#!/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두 개의 정렬 된 파일을 비교하고 3 개의 열로 출력합니다. 파일 # 1에 고유 한 항목에 대해서는 열 1, 파일 # 2에 고유 한 항목에 대해서는 열 2, 두 파일에있는 항목에 대해서는 열 3이 있습니다.

열을 억제하기 위해 스위치 -1, -2 및 -3을 사용할 수 있습니다. -3을 사용하면 다른 줄이 표시됩니다.

아래 명령의 스크린 샷을 볼 수 있습니다.

여기에 이미지 설명을 입력하십시오

하나의 요구 사항 만 있습니다. 파일을 올바르게 비교하려면 파일을 정렬해야합니다. sort그 목적으로 명령을 사용할 수 있습니다. Bellow는 파일을 정렬 한 다음 비교하는 또 다른 스크린 샷입니다. 왼쪽에서 시작하여 줄은 File_1에만, 2 열에서 시작하는 줄은 File_2에만 속합니다

여기에 이미지 설명을 입력하십시오


@DavidFoerster 그것은 모바일에서 편집하기가 좀 어렵습니다 :) 지금 완료
Sergiy Kolodyazhnyy

2

자식 설치 및 사용

$ git diff filename1 filename2

그리고 당신은 좋은 컬러 ​​형식으로 출력됩니다

힘내 설치

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

2

colcmp.sh

형식에서 두 파일의 이름 / 값 쌍을 비교합니다 name value\n. 는 기록 nameOutput_file변경 한 경우. 연관 배열 에는 bash v4 +가 필요합니다 .

용법

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

결과물 파일

$ 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

위의 경우는 성명서와 동일합니다. 내가 더 좋아하는 IDK.

출력 지우기

        echo "" > Output_File

위는 출력 파일을 지우므로 사용자가 변경되지 않은 경우 출력 파일이 비어 있습니다.

나는 내에서이 작업을 수행 할 경우 있도록 문 OUTPUT_FILE이 오류에 변경되지 않습니다.

사용자 파일을 쉘 스크립트로 복사

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

위에서 File_1.txt 를 현재 사용자의 홈 디렉토리로 복사 합니다.

예를 들어, 현재 사용자가 john 인 경우 위의 내용은 cp "File_1.txt"/home/john/.colcmp.arrays.tmp.sh와 같습니다.

특수 문자 탈출

기본적으로 저는 편집증입니다. 변수 할당의 일부로 스크립트에서 실행할 때 이러한 문자가 특별한 의미를 갖거나 외부 프로그램을 실행할 수 있음을 알고 있습니다.

  • `-back-tick-출력이 스크립트의 일부인 것처럼 프로그램과 출력을 실행합니다
  • $-달러 기호-일반적으로 변수 접두사
  • $ {}-보다 복잡한 변수 대체가 가능
  • $ ()-idk 이것이하는 일이지만 코드를 실행할 수 있다고 생각합니다.

내가 모르는 것은 bash에 대해 얼마나 많이 모른다 는 것입니다. 다른 문자가 특별한 의미를 갖는지 모르겠지만 백 슬래시로 모든 문자를 이스케이프 처리하고 싶습니다.

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

sed 정규 표현식 패턴 일치 보다 훨씬 더 많은 작업을 수행 할 수 있습니다. 스크립트 패턴 "s / (find) / (replace) /"는 구체적으로 패턴 일치를 수행합니다.

"s / (찾기) / (바꾸기) / (modifiers)"

영어 : 문장 부호 또는 특수 문자를 캡처 그룹 1 (\\ 1)로 캡처

  • (대체) = \\\\\\ 1
    • \\\\ = 리터럴 문자 (\\), 즉 백 슬래시
    • \ 1 = 캡처 그룹 1

영어로 : 모든 특수 문자 앞에 백 슬래시를 붙이십시오

  • (개질제) = g
    • g = 전체적으로 교체

영어로 : 같은 줄에 둘 이상의 일치하는 것이 있으면 모두 바꾸십시오.

전체 스크립트 주석 처리

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

위의 정규 표현식을 사용하여 ~ / .colcmp.arrays.tmp.sh 의 모든 줄 앞에 bash 주석 문자 ( # )를 입니다 . 나중에 내가 실행하려는 때문에 이렇게 ~ / .colcmp.arrays.tmp.sh을 사용하여 소스 내가 확신 전체 형식 모르기 때문에 명령을하고 File_1.txt .

실수로 임의의 코드를 실행하고 싶지 않습니다. 나는 아무도 생각하지 않습니다.

"s / (찾기) / (바꾸기) /"

영어로 : 각 줄을 캡처 그룹 1 (\\ 1)로 캡처

  • (대체) = # \\ 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"(두 번째 파일의 경우)

"s / (찾기) / (바꾸기) /"

영어로:

  • 선행 주석 문자 (#)는 필요하지만 무시하십시오.
  • 선행 공백 무시
  • 첫 번째 단어를 캡처 그룹 1 (\\ 1)로 캡처
  • 공백 (또는 탭 또는 공백)이 필요합니다
    • 이는 등호로 대체됩니다.
    • 캡처 그룹의 일부가 아니며
    • (바꾸기) 패턴은 캡처 그룹 1과 캡처 그룹 2 사이에 등호를 넣습니다.
  • 캡처 그룹 2로 나머지 라인을 캡처

  • (대체) = A1 \\ [\\ 1 \\] = \ "\\ 2 \"

    • A1 \\ [- A1[배열에서 배열 할당을 시작하는 리터럴 문자A1
    • \\ 1 = 캡처 그룹 1-선행 해시 (#)를 포함하지 않고 선행 공백을 포함하지 않습니다.이 경우 캡처 그룹 1은 bash 연관 배열에서 이름 / 값 쌍의 이름을 설정하는 데 사용됩니다.
    • \\] = \ "= 리터럴 문자 ]="
      • ]= 가까운 배열 할당, 예 : A1[User1 ]="US"
      • = = 대입 연산자 예 : 변수 = 값
      • " = 공백을 캡처하는 인용 값 ... ... 지금 생각하지만, 위의 코드를 백 슬래시 공백 문자로 백 슬래시하는 것이 더 쉬울 것입니다.
    • \\ 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

위에서 변수 대체를 사용하여 설정되지 않은 값과 길이가 0 인 문자열로 명시 적으로 설정된 변수 간의 차이를 감지합니다.

분명히 변수가 설정되었는지 확인 하는 많은 방법이 있습니다 . 나는 가장 많은 표를 얻은 사람을 선택했다.

                echo "$i has changed" > Output_File

위에서 $ i 사용자 를 Output_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

위에서 $ i 사용자 를 Output_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

위에서 $ i 사용자 를 Output_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.