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

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

File_1.txt:

User1 USUser2 USUser3 US

File_2.txt:

User1 USUser2 USUser3 NG

Выходной файл:

User3 has changed

Загляни в diff команда. Это хороший инструмент, и вы можете прочитать о нем все, набрав man diff в свой терминал.

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

enter image description here

Краткое замечание по чтению выходных данных третьей команды: "Стрелы" (< и >) обратитесь к тому, какое значение имеет строка в левом файле (<) против правильного файла (>), причем левый файл - это тот, который вы ввели первым в командной строке, в данном случае File_1.txt

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

Или вы можете использовать Разница в слиянии

Meld помогает сравнивать файлы, каталоги и проекты с контролем версий. Он обеспечивает двустороннее и трехстороннее сравнение как файлов, так и каталогов, а также поддерживает многие популярные системы контроля версий.

Установите, выполнив:

sudo apt-get install meld

Ваш пример:

enter image description here

Сравнить каталог:

enter image description here

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

enter image description here

Вы можете использовать vimdiff.

Пример:

vimdiff  file1  file2

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

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

дало бы что-то вроде:

User1 US                            User1 USUser2 US                            User2 USUser3 US                          | User3 NG

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

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

результатом будет

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

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

diffuse file1.txt file2.txt

enter image description here

Буквально придерживаясь вопроса (file1, file2, outputfile с сообщением "has changed"), приведенный ниже сценарий работает.

Скопируйте скрипт в пустой файл, сохраните его как compare.py, сделайте его исполняемым, запустите его с помощью команды:

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

Сценарий:

#!/usr/bin/env pythonimport sysfile1 = 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 pythonimport sysfile1 = sys.argv[1]; file2 = sys.argv[2]try:    outfile = sys.argv[3]except IndexError:    outfile = Nonedef 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"

Простой способ - использовать 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 опция дает унифицированную разницу. Вот как выглядит раскрашенный diff:

enter image description here

Устанавливать colordiff запустив sudo apt-get install colordiff.

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

$ git diff filename1 filename2

И вы получите вывод в красивом цветном формате

Мерзавец установка

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

colcmp.sh

Сравнивает пары имя/значение в 2 файлах в формате name value\n. Пишет name к Output_file если изменится. Требуется bash v4+ для ассоциативные массивы.

Использование

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

Выходной файл

$ cat Output_FileUser3 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 заявление для оценки $? потому что ценность $? изменения после каждой команды, включая тест ([).

В качестве альтернативы я мог бы использовать переменную для хранения значения $?:

cmp -s "$1" "$2"CMPRESULT=$?if [ $CMPRESULT -eq 0 ]; then    # matchelif [ $CMPRESULT -eq 1 ]; then    # compareelse    # errorfi

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

Очистите выходные данные

        echo "" > Output_File

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

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

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

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

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

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

Экранирование Специальных Символов

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

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

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

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

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

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

по-английски: фиксируйте любые знаки препинания или специальные символы как caputure group 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/(найти)/(заменить)/"

по-английски: захватите каждую строку как caputure group 1 (\\1)

  • (заменить) = #\\1

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

Преобразовать значение пользователя в A1[User]="значение"

        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 group 1 (\\1)
  • требуется пробел (или табуляция, или пробел)
    • это будет заменено знаком равенства, потому что
    • это не часть какой-либо группы захвата, и потому
    • шаблон (replace) ставит знак равенства между группой захвата 1 и группой захвата 2
  • захватите остальную часть линии в качестве группы захвата 2

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

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

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

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

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

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

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

Объявить ассоциативный массив (bash v4+)

        declare -A A1

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

Вот почему для скрипта требуется версия bash версии 4 или выше.

Выполните наш Сценарий Назначения переменной массива

        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

Выше добавляет пользователя к Выходной файл

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

        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]}'"

Потому что является ли ключ массива (имя пользователя) $A2[$i] должен возвращать значение, связанное с текущим пользователем из File_2.txt.

Например, если является Пользователь1, вышеизложенное выглядит следующим образом ${A2[Пользователь1]}

                echo "$i has changed" > Output_File

Выше добавляет пользователя к Выходной файл

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

Потому что является ли ключ массива (имя пользователя) $A1[$i] должен возвращать значение, связанное с текущим пользователем из File_1.txt, и $A2[$i] должен возвращать значение из File_2.txt.

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

                echo "$i has changed" > Output_File

Выше добавляет пользователя к Выходной файл

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

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

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

Выше сообщается о значении $USERS, КОТОРЫЕ НЕ ОБМЕНИВАЮТСЯ но только в том случае, если есть ценность в $USERS, КОТОРЫЕ НЕ ОБМЕНИВАЮТСЯ. То, как это написано, $USERS, КОТОРЫЕ НЕ ОБМЕНИВАЮТСЯ не может содержать никаких пробелов. Если ему действительно нужны пробелы, вышеизложенное можно переписать следующим образом:

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

Используйте "diff "File_1.txt " "File_2.txt "`

Также посетите: software recommendation - File and directory comparison tool? - Ask Ubuntu