Отже, в основному я хочу зробити порівняння двох файлів за рядком за стовпцем 2. Як я можу це зробити?
Файл_1.txt:
User1 US
User2 US
User3 US
Файл_2.txt:
User1 US
User2 US
User3 NG
Вихідний_файл:
User3 has changed
Отже, в основному я хочу зробити порівняння двох файлів за рядком за стовпцем 2. Як я можу це зробити?
Файл_1.txt:
User1 US
User2 US
User3 US
Файл_2.txt:
User1 US
User2 US
User3 NG
Вихідний_файл:
User3 has changed
Відповіді:
Погляньте на diff
команду. Це хороший інструмент, і ви можете прочитати все про нього, ввівши man diff
свій термінал.
Команда, яку ви хочете зробити, - diff File_1.txt File_2.txt
це вивести різницю між обома і має виглядати приблизно так:
Коротка примітка про читання результатів з третьої команди: "Стрілки" ( <
і >
) посилаються на те, яке значення рядка знаходиться у лівому файлі ( <
) та в правому файлі ( >
), причому лівий файл є тим, що ви ввели спочатку в командному рядку, в цьому випадкуFile_1.txt
Крім того, ви можете помітити, що четверта команда - diff ... | tee Output_File
це передача результатів diff
в а tee
, яка потім виводить цей висновок у файл, щоб ви могли зберегти його на потім, якщо ви не хочете переглянути все на консолі право тієї секунди.
diff file1 file2 -s
. Ось приклад: imgur.com/ShrQx9x
Або ви можете використовувати Meld Diff
Meld допомагає вам порівнювати файли, каталоги та проекти, керовані версіями. Він забезпечує дво- та тристороннє порівняння і файлів, і каталогів, а також підтримує багато популярних систем управління версіями.
Встановити, запустивши:
sudo apt-get install meld
Ваш приклад:
Порівняйте каталог:
Приклад з повним текстом:
dos
а друге - у unix
.
FWIW, мені більше подобається те, що я отримую при виводі набік від розл
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
якщо все, що ви хочете, це повернення коду.
Літерально дотримуючись питання (файл1, файл2, вихідний файл із повідомленням "змінився") працює сценарій нижче.
Скопіюйте скрипт у порожній файл, збережіть його як 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
Порівняє пари імен / значень у двох файлах у форматі 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 встановить значення $? в наступному :
Я вирішив використовувати випадок case .. esac для оцінки $? тому що значення $? зміни після кожної команди, включаючи тест ([).
Як варіант, я міг би використовувати змінну, щоб утримувати значення $? :
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 до домашнього режиму поточного користувача.
Наприклад, якщо поточний користувач john, вищевикладене буде таким же, як cp "File_1.txt" /home/john/.colcmp.arrays.tmp.sh
В основному я параноїк. Я знаю, що ці символи можуть мати особливе значення або виконувати зовнішню програму під час запуску в скрипті в рамках присвоєння змінної:
Чого я не знаю - це те, наскільки я не знаю про баш. Я не знаю, які інші символи можуть мати особливе значення, але я хочу уникнути їх усією зворотною косою рисою:
sed -i -E "s/([^A-Za-z0-9 ])/\\\\\\1/g" ~/.colcmp.array1.tmp.sh
sed може зробити набагато більше, ніж звичайне зіставлення шаблону виразу . Шаблон сценарію "s / (знайти) / (замінити) /" спеціально виконує відповідність шаблону.
"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"