Итак, в основном то, что я хочу сделать, это сравнить два файла по строкам по столбцу 2. Как я мог бы этого добиться?
File_1.txt:
User1 USUser2 USUser3 US
File_2.txt:
User1 USUser2 USUser3 NG
Выходной файл:
User3 has changed
Итак, в основном то, что я хочу сделать, это сравнить два файла по строкам по столбцу 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
который выведет разницу между ними и должен выглядеть примерно так:
Краткое замечание по чтению выходных данных третьей команды: "Стрелы" (<
и >
) обратитесь к тому, какое значение имеет строка в левом файле (<
) против правильного файла (>
), причем левый файл - это тот, который вы ввели первым в командной строке, в данном случае 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 changed
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 установит значение $? как следует:
Я решил использовать дело..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 US
A1[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 "`