Поэтому я хочу сравнить два файла строка за столбцом 2. Как я могу это сделать?
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
Выходной файл:
User3 has changed
Поэтому я хочу сравнить два файла строка за столбцом 2. Как я могу это сделать?
File_1.txt:
User1 US
User2 US
User3 US
File_2.txt:
User1 US
User2 US
User3 NG
Выходной файл:
User3 has changed
Ответы:
Посмотри в diff
команду. Это хороший инструмент, и вы можете прочитать все об этом, набрав man diff
в своем терминале.
Команда, которую вы захотите сделать, diff File_1.txt File_2.txt
выведет разницу между ними и должна выглядеть примерно так:
Краткое примечание о прочтении вывода третьей команды: «стрелки» ( <
и >
) относятся к значению строки в левом файле ( <
) по сравнению с правым файлом ( >
), причем левый файл - тот, который вы ввели сначала в командной строке, в этом случаеFile_1.txt
Кроме того, вы можете заметить, что 4-ая команда diff ... | tee Output_File
передает эти результаты diff
в a tee
, который затем помещает этот вывод в файл, так что вы можете сохранить его на потом, если не хотите просматривать все это на консоли прямо в эту секунду.
diff file1 file2 -s
. Вот пример: imgur.com/ShrQx9x
Или вы можете использовать Meld Diff
Meld поможет вам сравнить файлы, каталоги и проекты с управлением версиями. Он обеспечивает двух- и трехстороннее сравнение файлов и каталогов, а также поддерживает многие популярные системы контроля версий.
Установить, запустив:
sudo apt-get install meld
Ваш пример:
Сравнить каталог:
Пример с полным текстом:
dos
а второй - unix
.
FWIW, мне скорее нравится то, что я получаю с параллельным выводом из diff
diff -y -W 120 File_1.txt File_2.txt
дал бы что-то вроде:
User1 US User1 US
User2 US User2 US
User3 US | User3 NG
Вы можете использовать команду cmp
:
cmp -b "File_1.txt" "File_2.txt"
выход будет
a b differ: byte 25, line 3 is 125 U 116 N
cmp
гораздо быстрее, чем diff
если бы все, что вам нужно, это код возврата.
Придерживаясь вопроса (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"
Простым способом является использование 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
опция дает унифицированный дифференциал Вот как выглядит цветная разница:
Установить colordiff
, запустив sudo apt-get install colordiff
.
Если нет необходимости знать, какие части файлов различаются, вы можете использовать контрольную сумму файла. Есть много способов сделать это, используя md5sum
или sha256sum
. По сути, каждый из них выводит строку, к которой относится хэш содержимого файла. Если два файла одинаковы, их хэш будет одинаковым. Это часто используется при загрузке программного обеспечения, такого как установочные образы 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 столбцах: столбец 1 для элементов, уникальных для файла № 1, столбец 2 для элементов, уникальных для файла № 2, и столбец 3 для элементов, присутствующих в обоих файлах.
Для подавления любого столбца вы можете использовать переключатели -1, -2 и -3. Использование -3 покажет линии, которые отличаются.
Ниже вы можете увидеть скриншот команды в действии.
Существует только одно требование - файлы должны быть отсортированы для их правильного сравнения. sort
Команда может быть использована для этой цели. Ниже приведен скриншот, где файлы сортируются, а затем сравниваются. Строки, начинающиеся с левого столбца только для File_1, строки, начинающиеся со столбца 2, принадлежат только File_2
Установите git и используйте
$ git diff filename1 filename2
И вы получите вывод в хорошем цветном формате
Git установка
$ apt-get update
$ apt-get install git-core
Сравнивает пары имя / значение в 2 файлах в формате name value\n
. Записывает name
в Output_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
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
# match
elif [ $CMPRESULT -eq 1 ]; then
# compare
else
# error
fi
Выше делает то же самое, что и утверждение дела. ИДК, который мне нравится больше.
echo "" > Output_File
Выше очищает выходной файл, поэтому, если ни один пользователь не изменился, выходной файл будет пустым.
Я делаю это внутри инструкций case, чтобы файл Output_file оставался неизменным при ошибке.
cp "$1" ~/.colcmp.arrays.tmp.sh
Выше копирует File_1.txt в домашнюю директорию текущего пользователя.
Например, если текущий пользователь является пользователем john, приведенное выше будет таким же, как cp "File_1.txt" /home/john/.colcmp.arrays.tmp.sh
По сути, я параноик. Я знаю, что эти символы могут иметь особое значение или выполнять внешнюю программу при запуске в сценарии как часть назначения переменной:
Чего я не знаю, так это того, как много я не знаю о bash. Я не знаю, какие другие символы могут иметь особое значение, но я хочу избежать их всех с помощью обратной косой черты:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed может сделать гораздо больше, чем сопоставление с шаблоном регулярного выражения . Шаблон сценария "s / (find) / (replace) /" специально выполняет сопоставление шаблона.
"S / (поиск) / (заменить) / (модификаторы)"
на английском языке: запишите любую пунктуацию или специальный символ в качестве группы будущего 1 (\\ 1)
по-английски: префикс всех специальных символов с обратной косой чертой
на английском: если в одной строке найдено более одного совпадения, замените их все
sed -i -E "s/^(.*)$/#\\1/" ~/.colcmp.arrays.tmp.sh
Выше используется регулярное выражение для префикса каждой строки ~ / .colcmp.arrays.tmp.sh с символом комментария bash ( # ). Я делаю это потому, что позже я намереваюсь выполнить ~ / .colcmp.arrays.tmp.sh, используя команду source, и потому что я точно не знаю весь формат File_1.txt .
Я не хочу случайно выполнять произвольный код. Я не думаю, что кто-то делает.
"S / (поиск) / (заменить) /"
на английском: запишите каждую строку как группу будущего 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[
User1 ]="
US"
=
= оператор присваивания, например, переменная = значение"
= значение кавычки для захвата пробелов ... хотя теперь, когда я думаю об этом, было бы проще позволить приведенному выше коду, который ставит обратную косую черту, также и символы обратной косой черты.на английском: замените каждую строку в формате #name value
оператором присваивания массива в форматеA1[name]="value"
chmod 755 ~/.colcmp.arrays.tmp.sh
Выше используется chmod, чтобы сделать исполняемый файл скрипта массива.
Я не уверен, если это необходимо.
declare -A A1
Заглавная -A указывает, что объявленные переменные будут ассоциативными массивами .
Вот почему скрипт требует bash v4 или выше.
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
Выше добавляет пользователя $ 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
diff "File_1.txt" "File_2.txt"