Итак, в основном то, что я хочу сделать, это сравнить два файла по строкам по столбцу 2. Как я мог бы этого добиться?
File_1.txt:
User1 USUser2 USUser3 USFile_2.txt:
User1 USUser2 USUser3 NGВыходной файл:
User3 has changed
Итак, в основном то, что я хочу сделать, это сравнить два файла по строкам по столбцу 2. Как я мог бы этого добиться?
File_1.txt:
User1 USUser2 USUser3 USFile_2.txt:
User1 USUser2 USUser3 NGВыходной файл:
User3 has changed
Загляни в diff команда. Это хороший инструмент, и вы можете прочитать о нем все, набрав man diff в свой терминал.
Команда, которую вы захотите выполнить, это diff File_1.txt File_2.txt который выведет разницу между ними и должен выглядеть примерно так:

Краткое замечание по чтению выходных данных третьей команды: "Стрелы" (< и >) обратитесь к тому, какое значение имеет строка в левом файле (<) против правильного файла (>), причем левый файл - это тот, который вы ввели первым в командной строке, в данном случае File_1.txt
Кроме того, вы можете заметить, что 4-я команда - это diff ... | tee Output_File это передает результаты из diff в один tee, который затем помещает эти выходные данные в файл, чтобы вы могли сохранить их на потом, если не хотите просматривать все это на консоли прямо в эту секунду.
Или вы можете использовать Разница в слиянии
Meld помогает сравнивать файлы, каталоги и проекты с контролем версий. Он обеспечивает двустороннее и трехстороннее сравнение как файлов, так и каталогов, а также поддерживает многие популярные системы контроля версий.
Установите, выполнив:
sudo apt-get install meldВаш пример:

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

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

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
Буквально придерживаясь вопроса (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:
Устанавливать colordiff запустив sudo apt-get install colordiff.
Устанавливать мерзавец и использовать
$ git diff filename1 filename2И вы получите вывод в красивом цветном формате
Мерзавец установка
$ apt-get update$ apt-get install git-core
Сравнивает пары имя/значение в 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 changedcmp -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 ;;esaccmp установит значение $? как следует:
Я решил использовать дело..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
По сути, я параноик. Я знаю, что эти символы могут иметь особое значение или выполнять внешнюю программу при запуске в скрипте как часть присваивания переменной:
что я не знаю вот как много я не знаю о баше. Я не знаю, какие другие символы могут иметь особое значение, но я хочу избежать их всех с помощью обратной косой черты:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.shсед может сделать гораздо больше, чем сопоставление шаблонов регулярных выражений. Шаблон сценария "s/(найти)/(заменить)/" специально выполняет сопоставление с шаблоном.
"s/(найти)/(заменить)/(модификаторы)"
по-английски: фиксируйте любые знаки препинания или специальные символы как caputure group 1 (\\1)
по-английски: префикс всех специальных символов с обратной косой чертой
по-английски: если в одной строке найдено более одного совпадения, замените их все
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)
по-английски: замените каждую строку символом фунта, за которым следует строка, которая была заменена
sed -i -E "s/^#\\s*(\\S+)\\s+(\\S.*?)\\s*\$/A1\\[\\1\\]=\"\\2\"/" ~/.colcmp.arrays.tmp.shВыше приведено ядро этого скрипта.
#User1 USA1[User1]="US"
A2[User1]="US" (для 2-го файла)"s/(найти)/(заменить)/"
по-английски:
захватите остальную часть линии в качестве группы захвата 2
(заменить) = A1\\[\\1\\]=\"\\2\"
A1[ чтобы начать назначение массива в массиве, вызываемом A1
]="] = закрыть назначение массива, например A1[Пользователь1]="США"
= = оператор присваивания, например, переменная=значение" = значение кавычки для захвата пробелов ... хотя теперь, когда я думаю об этом, было бы проще позволить приведенному выше коду, который использует обратную косую черту, также использовать пробелы с обратной косой чертой.по-английски: замените каждую строку в формате #name value с помощью оператора присваивания массива в формате A1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.shВышеуказанные виды использования chmod чтобы сделать файл скрипта массива исполняемым.
Я не уверен, что это необходимо.
declare -A A1Заглавная буква A указывает, что объявленные переменные будут ассоциативные массивы.
Вот почему для скрипта требуется версия bash версии 4 или выше.
source ~/.colcmp.arrays.tmp.shМы уже:
User value к линиям A1[User]="value", Над нами источник сценарий для его запуска в текущей оболочке. Мы делаем это для того, чтобы сохранить значения переменных, которые задаются скриптом. Если вы выполняете скрипт напрямую, он порождает новую оболочку, и значения переменных теряются при выходе из новой оболочки, или, по крайней мере, я так понимаю.
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 "`