Как сравнить два файла

Так в основном то, что я хочу сделать, сравнивают два файла с методической точностью столбцом 2. Как я мог выполнить это?

File_1.txt:

User1 US
User2 US
User3 US

File_2.txt:

User1 US
User2 US
User3 NG

Output_File:

User3 has changed
88
задан 29 November 2018 в 22:11

11 ответов

Изучите эти diff команда. Это - хороший инструмент, и можно считать все об этом путем ввода man diff в терминал.

команда, которую Вы захотите сделать, diff File_1.txt File_2.txt, который произведет различие между двумя и должен выглядеть примерно так:

enter image description here

А быстрое примечание по чтению вывода от третьей команды: 'стрелки' (< и >) обращаются к тому, что значение строки находится в левом файле (<) по сравнению с правильным файлом (>) с левым файлом, являющимся тем, который Вы ввели сначала в командную строку, в этом случае File_1.txt

Дополнительно Вы могли бы заметить, что 4-я команда diff ... | tee Output_File, это передает результаты по каналу diff в tee, который тогда помещает тот вывод в файл, так, чтобы можно было на потом сохранить его, если Вы не хотите просматривать все это на консоли прямо в ту секунду.

96
ответ дан 22 November 2019 в 23:10

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")

С несколькими дополнительными строками можно сделать его или печатью к outputfile, или к терминалу, в зависимости от того, если outputfile определяется:

Распечатать в файл:

/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"
7
ответ дан 22 November 2019 в 23:10

Или можно использовать Разность Комбинации

, Комбинация помогает Вам сравнить файлы, каталоги, и версия управляла проектами. Это обеспечивает два - и сравнение с тремя путями и файлов и каталогов, и имеет поддержку многих популярных систем управления версиями.

Установка путем выполнения:

sudo apt-get install meld

Ваш пример:

enter image description here

каталог Compare:

enter image description here

Пример с полным из текста:

enter image description here

36
ответ дан 22 November 2019 в 23:10

Можно использовать vimdiff.

Пример:

vimdiff  file1  file2
18
ответ дан 22 November 2019 в 23:10

FWIW, я скорее как то, что я получаю с бок о бок выводом от разности

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

, дал бы что-то как:

User1 US                            User1 US
User2 US                            User2 US
User3 US                          | User3 NG
14
ответ дан 22 November 2019 в 23:10

Meld действительно большой инструмент. Но можно также использовать diffuse для визуального сравнения двух файлов:

diffuse file1.txt file2.txt

enter image description here

8
ответ дан 22 November 2019 в 23:10

Простой способ состоит в том, чтобы использовать colordiff, который ведет себя как diff, но colorizes его вывод. Это очень полезно для чтения diffs. Используя Ваш пример,

$ 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 опция дает объединенное различное. Это - то, как цветная разность похожа:

enter image description here

Установка colordiff путем выполнения sudo apt-get install colordiff.

4
ответ дан 22 November 2019 в 23:10

Можно использовать команду cmp :

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

вывод был бы

a b differ: byte 25, line 3 is 125 U 116 N
10
ответ дан 22 November 2019 в 23:10

Дополнительный ответ

, Если нет никакой потребности знать, какие части файлов отличаются, можно использовать контрольную сумму файла. Существует много способов сделать это, с помощью md5sum или sha256sum. В основном каждый из них производит строку, к которой файл содержание хеширует. Если эти два файла будут тем же, их хеш совпадет с хорошо. Это часто используется при загрузке программного обеспечения, такого как изображения ISO установки Ubuntu. Они часто используются для проверки целостности загруженного содержания.

Рассматривают сценарий ниже, где можно дать два файла как аргументы, и файл скажет Вам, если они будут тем же или нет.

#!/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 colums: столбец 1 для объектов, уникальных для файла № 1, столбца 2 для объектов, уникальных для файла № 2 и столбца 3 для объектов, существующих в обоих файлах.

Для подавления любого столбца можно использовать переключатели-1,-2, и-3. Используя-3 покажет строки, которые отличаются.

Bellow Вы видите снимок экрана команды в действии.

enter image description here

существует всего одно требование - файлы должны быть отсортированы для них, чтобы быть сравненными правильно. sort команда может использоваться с этой целью. Bellow является другим снимком экрана, где файлы отсортированы и затем сравнены. Строки, запускающиеся слева, принадлежат File_1 только, строки, запускающиеся на столбце 2, принадлежат File_2 [только 1 117]

enter image description here

2
ответ дан 22 November 2019 в 23:10

Установите мерзавца и использование

$ git diff filename1 filename2

И Вы будете произведены в хорошем цветном формате

Установка мерзавца

$ apt-get update
$ apt-get install git-core
2
ответ дан 22 November 2019 в 23:10

colcmp.sh

Сравнивает пары имя/значение в 2 файлах в формате name value\n. Записи name кому: Output_file если изменено. Требует удара 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

Выше делает то же самое как оператор выбора. IDK, который я люблю лучше.

Очистите вывод

        echo "" > Output_File

Выше очищает выходной файл поэтому, если никакие пользователи не изменились, выходной файл будет пуст.

Я делаю эту внутреннюю часть операторы выбора так, чтобы Output_file остался неизменным на ошибке.

Скопируйте пользовательский файл в сценарий оболочки

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

Выше копий File_1.txt к домашнему dir текущего пользователя.

Например, если бы текущим пользователем является Джон, вышеупомянутое совпало бы с CP "File_1.txt"/home/john/.colcmp.arrays.tmp.sh

Выйдите из специальных символов

В основном я параноик. Я знаю, что эти символы могли иметь особое значение или выполнить внешнюю программу, когда выполнено в сценарии как часть переменного присвоения:

  • '-обратная галочка - выполняет программу и вывод, как будто вывод был частью Вашего сценария
  • $ - знак доллара - обычно префиксы переменная
  • $ {} - допускает замену более комплексной переменной
  • $ () - idk, что это делает, но я думаю, что он может выполнить код

То, что я не знаю, - то, сколько я не знаю об ударе. Я не знаю то, что другие символы могли бы иметь особое значение, но я хочу выйти из них всех с обратной косой чертой:

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

sed может сделать намного больше, чем соответствие образца регулярного выражения. Шаблон сценария "s / (находит) / (замена) /" конкретно выполняет соответствие шаблона.

"s / (находят) / (замена) / (модификаторы)"

на английском языке: получите любой знак пунктуации или специальный символ как caputure группа 1 (\\1)

на английском языке: префикс все специальные символы с обратной косой чертой

  • (модификаторы) = g
    • g = глобально замена

на английском языке: если больше чем одно соответствие найдено на той же строке, замените их всех

Прокомментируйте весь сценарий

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

Выше использования регулярное выражение для добавления префикса каждой строки ~/.colcmp.arrays.tmp.sh с символом комментария удара (#). Я делаю это, потому что позже я намереваюсь выполнить ~/.colcmp.arrays.tmp.sh использование исходной команды и потому что я не знаю наверняка целый формат File_1.txt.

Я не хочу случайно выполнять произвольный код. Я не думаю, что любой делает.

"s / (находят) / (замена) /"

на английском языке: получите каждую строку как caputure группа 1 (\\1)

на английском языке: замените каждую строку символом фунта, сопровождаемым строкой, которая была заменена

Преобразуйте пользовательское значение в A1 [пользователь] = "значение"

        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 / (находят) / (замена) /"

на английском языке:

  • потребуйте, но проигнорируйте ведущие символы комментария (#)
  • проигнорируйте ведущий пробел
  • получите первое слово как caputure группа 1 (\\1)
  • потребуйте пространства (или вкладка или пробел)
    • это будет заменено, равняется знаку потому что
    • это не часть никакой группы получения, и потому что
    • (замена) шаблон помещает, равняется знаку между группой 1 получения и группой 2 получения
  • остальная часть получения строки как группа 2 получения

  • (замена) = A1 \\[\\1 \\] = \"\\2 \"

    • A1 \\[-буквенные символы A1[ запустить присваивание массива в названном массиве A1
    • \\1 = получают группу 1 - который не включает ведущий хеш (#) и не включает ведущий пробел - в эту группу 1 получения случая, используется для определения имени пары имя/значение в ассоциативном массиве удара.
    • \\] = \" = буквенные символы ]="
      • ] = близкое присваивание массива, например. A1[User1]="США"
      • = = оператор присваивания, например, variable=value
      • " = заключите значение в кавычки для получения пробелов..., хотя теперь, когда я думаю об этом, было бы легче позволить коду выше этого обратные косые черты все к также пробелам обратной косой черты.
    • \\1 = получают группу 2 - в этом случае, значение пары имя/значение
    • "= закрывающий значение кавычки для получения пробелы

на английском языке: замените каждую строку в формате #name value с оператором присваивания массива в формате A1[name]="value"

Сделайте исполняемый файл

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

Выше использования chmod для создания исполняемого файла файла сценария массива.

Я не уверен, необходимо ли это.

Объявите Ассоциативный массив (колотите v4 +),

        declare -A A1

Капитал-A указывает, что объявленные переменные будут ассоциативными массивами.

Поэтому сценарий требует удара 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

Мы делаем то же самое за 1$ и A1, который мы делаем за 2$ и A2. Это действительно должна быть функция. Я думаю в этой точке, этот сценарий путает достаточно, и это работает, таким образом, я не собираюсь фиксировать его.

Обнаружьте удаленных пользователей

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

Выше циклов через ключи ассоциативного массива

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

Выше подстановки переменных использования для обнаружения различия между значением, которое сброшено по сравнению с переменной, которая была явно установлена на строку нулевой длины.

По-видимому, существует много способов видеть, была ли переменная установлена. Я выбрал тот с большинством голосов.

                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.

Например, если $i является User1, вышеупомянутыми чтениями как $ {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
2
ответ дан 22 November 2019 в 23:10

Другие вопросы по тегам:

Похожие вопросы: