Yellow leaf - статті - використання команд diff і patch

Комманди diff і patch представляють собою потужну комбінацію. Вони часто використовуються для отримання різниці між оригінальними файлами і оновленими, що б інші люди, у яких є тільки лише оригінальні файли могли оновити їх використовуючи файли, які містять тільки різницю. У цій статті показані базові принципи використання цих команд.

Використання diff для створення простого патча

Найбільш простий приклад використання команди diff - отримання відмінностей між двома файлами, оригінальним і оновленим. Можете, наприклад, написати наскільки слів звичайного тексту, зробити які-небудь зміни, і зберегти зміни в другій файл. Тепер ви можете порівняти ці ці два файли, використовуючи команду diff:

Звичайно, треба замінити originalfile і updatedfile відповідними іменами файлів. В результаті має вийти щось на зразок цього:

Зверніть увагу: Що б продемонструвати створення простого патча, я використовував оригінальний файл, що містить рядок "These are a few words.", І змінений файл, що містить рядок "These still are just a few words." Ви можете створити ці файли самі, якщо хочете запустити команду зі статті і отримати той же результат.

1c1 показує номер рядка і те, що з нею треба зробити. Зверніть увагу, що може бути відразу декілька рядків (наприклад, 12,15, що означає з рядка 12 до рядка 15). Символ "c" означає, що патч замінить цей рядок. Є ще два інших символу: "a" і "d". Вони означають "додати" (add) і "видалити" (delete) відповідно. Таким чином, синтаксис наступний: (номер рядка або діапазон рядків) (c, a або d) (номер рядка або діапазон рядків), хоча коли використовуються "a" або "d", одна з частин (номер рядка або діапазон рядків) може містити тільки номер однієї рядки.

Коли використовується "c", номера рядків зліва - це рядки в оригінальному файлі, які треба замінити рядками, які перебувають в патчі, а номери рядків справа - це рядки, які повинні бути в пропатченний файлі.

Коли використовується "a", номер зліва може бути тільки номером одного рядка, який показує, де треба додати рядок в пропатченний файлі, а номери рядків справа - це рядки, які повинні бути в пропатченний файлі.

Коли використовується "d", номера рядків зліва - це рядки, які треба видалити, щоб отримати пропатченних версію файлу, а номер рядка праворуч може бути тільки номером одного рядка, який показує де будуть рядки в пропатченний файлі, якщо вони не будуть видалені. Ви можете подумати, що останній номер не потрібен, але не забувайте, що патч можна застосувати для восстаноленія вихідного файлу. Це буде пояснено пізніше.

знак "<" означает, что патч должен удалить символы после этого знака, а знак ">"Означає, що символи після цього знака треба додати. Коли треба замінити рядки (" c "між номерами рядків), ви побачите обидва знака: і"<", и ">". Коли треба додати рядок (" a "між номерами рядків), ви побачите тільки знак"> ", а коли треба видалити рядок (" d "між номерами рядків), ви побачите тільки знак"<".

Рядок "\ No newline at end of file" з'явилася через те, що я не не натиснула enter після того як набрав слова. Вважається хорошим тоном закінчувати текстовий файл символом нового рядка. Деяким програмам вона необхідна для роботи. Тому цей рядок з'явилася після роботи команди diff. Додамо порожні рядки в кінець файлів, і отримаємо більш короткий висновок команди diff:

Як ви можливо помітили, я не пояснив що означають 3 знаки "-". Вони означають кінець рядків, які треба замінити і початок рядків на які треба замінити. Поділ старих і нових рядків. Ви побачите це знак тільки при заміні ( "c" між номерами рядків).

Знову ж таки не забудьте замінити originalfile і updatedfile на відповідні імена файлів. Ви напевно знаєте, що опція bash ">" працює з усіма командами. Це дуже корисна властивість.

Застосування простого патча, який ми створили

Ми можемо використовувати патч, який тільки що створили, щоб отримати з оригінального файлу оновлений. Для цього треба скопіювати оригінальний файл і патч в одне і теж місце. І потім застосувати патч:

Природно, і тут треба змінити імена файлів на необхідні. Якщо все пройшло добре, повинен вийти файл, ідентичний оновленим. Ви можете переконатися в цьому, використовуючи команду diff з опцією "-s":

Замініть текст між [і] на шлях до оригінального файлу. Наприклад, якщо оновлений файл, який ви використовували при створенні патча знаходиться в батьківській директорії вишу поточної, то "[/ path / to / the / original / updatedfile]" треба замінити на ".." (bash розуміє це як батьківську директорію від поточної ). І звичайно треба змінити імена файлів на вірні.

Вітаю! Якщо diff повідомила, що файли ідентичні, ви тільки що успішно створили і застосували патч! Однак формат патча, який ми тільки що використовували не єдиний. У наступному розділі ми розглянемо інший формат патча.

У першому розділі ми створили патч, використовуючи нормальний формат команди diff. Однак цей формат не забезпечує контекстної залежності, а використовує рядка цілком. Створимо патч для того ж файлу, але використовуючи контектсний формат:

Результат вийде наступний:

Як ви бачите, тут включено ім'я файлу. Це означає, що нам не доведеться набирати його під час застосування патча. Далі йде дата і час останньої зміни файлу. рядок з 15 "*" показує початок змін. Вони показують, що треба зробити з наступним блоком тексту. Два номери 1 - це номери рядків (тут теж може бути відразу кілька рядків), а "!" означає, що рядки треба замінити. Рядок з "!" перед трьома "-" повинна бути замінена другим рядком з "!", яка йде після трьох "-" (звичайно сам. не буде включено; це синтаксис контекстного формату). Як ви можете бачити, тут немає знаків "c", "a" і "d" .Дія, яке потрібно зробити, визначається символом на початку рядка. "!" означає заміну. Інші символи - "+", "-" і "" (пробіл). "+" Означає додавання, "-" означає видалення, а "" означає нічого не робити: патч використовує його щоб переконатися, що він змінює правильну частину файлу.

Застосовувати цей патч легше: при тих же умовах, що і для попереднього патча (записуємо висновок команди diff в файл, потім копіюємо патч і оригінал в один і той же місце), треба виконати наступну команду:

Ви можливо зараз думаєте: навіщо нам треба вказувати ім'я нового файлу? Це треба зробити через те, що патч намагається змінити існуючий файл, а не створює новий. Це зручно при створенні патча для декількох файлів відразу. Це призводить нас до наступної мети: створення патча для дерева файлів. Розглянемо це в наступному розділі.

Отримання відмінностей між декількома файлами

Найбільш простий спосіб отримати відмінності між декількома файлами - це покласти їх в одну директорію і виконати команду diff для цієї теки цілком. Ви можете просто передати команді diff в якості параметрів імена директорій замість імен файлів:

Зверніть увагу: Якщо в директорія є піддиректорії, то треба використовувати опцію "-r".

В результаті маємо отримати щось на зразок цього:

Як ви бачите, нормальний формат містить тільки імена файлів і змінювані рядки.

Тепер використовуємо контекстний формат:

Перша річ, яку ви повинні були помітити - це збільшення розміру; контекстний формат містить більше інформації, ніж нормальний. Цього не було помітно в першому прем'єрки, так як не було контексту. Однак тепер контекст є, і за рахунок нього розмір патча збільшився. Крім того, ви напевно помітили, що ім'я файлу повторюється двічі. Це можливо зроблено для того, щоб легше було зрозуміти коли почався патч наступного файлу або для забезпечення кращого відновлення.

Інший спосіб отримати різницю між між декількома файлами - це написати скрипт, який виконує команду diff кілька разів і додає результат виконання в один файл. Ми не будемо розглядати цей спосіб, так як покласти всі файли в одну директорію горазда простіше.

Створити патч було легко, але використання директорій ставить наступну проблему: бедут чи патч змінювати тільки відповідні файли в поточному каталозі, або буде використовувати відповідний шлях, вказаний у файлі? Щоб дізнатися це, дивіться наступний розділ!

Застосування патча до декількох файлів

У попередньому розділі ми створили патч для декількох файлів, скориставшись наступною командою:

Зверніть увагу: ми використовуємо контекстний формат патча, так як це є хорошим тоном.

Тепер треба використовувати отриманий патч. Скопіюйте оригінальну директорію і патч куди-небудь і застосуєте таку команду:

Однак виникає помилка, що неможливо знайти файли для патча. Команда намагається знайти файл file1 в поточній директорії (за замовчуванням патч прибирає всі шляхи перед ім'ям файлу). І конено файлу немає, так як ми намагаємося відновити файли в директорії originaldirectory. Тому ми повинні змусити патч використовувати повний шлях. Це робиться в такий спосіб:

Зверніть увагу: Ви може подумати, що можна просто переміститися в originaldirectory і запустити патч. Але це не так! Так робити не варто: якщо в в патчі містяться піддиректорії, то він буде шукати їх в робочій директорії, і не знайде, чи знайде не ті. Використовуйте опцію "-p", щоб змусити патч шукати файли в піддиректоріях.

Опція "-p" говорить патч скільки Слеш (включаючи те, що перед ними, зазвичай директорії) потрібно вирізати перед ім'ям файлу (зверніть увагу, що при використанні опції "-p0", патч буде шукатиме файли і в originaldirectory і в updateddirectory) .Коли ми встановлюємо 0, це означає що не треба видаляти шляху, але можна поставити 1, щоб видалити перший слеш, або 2, щоб видалити два слеша, і т.д. Це може бути корисно, якщо якщо в патчі використовується структура каталогів, відмінна від вашої. Наприклад, якщо в патчі використовується наступна структура каталогів:

Вам треба просто порахувати кількість Слеш (/ (1) home / (2) username / (3) sources / (4) program / (5)) і передати це число в опціе "-p". Якщо ви використовуєте "-p5", то патч буде шукати і в originaldirectory / file1 і в updateddirectory / file1. Не забудьте, що патч розглядає два слеша один за одним (як в / home / username // sources) як один. Це викликано тим, що іноді патч скрипти додають додатковий слеш між директоріями.

Відновлення оригінального файлу з пропатченний

Іноді виникає необхідність відновити оригінальний файл з пропатченний. Наприклад, якщо в ньому міститься помилка. Для цього треба використовувати опцію "-R":

Зазвичай ця операція проходить без помилок, і ви отримуєте вихідний файл. Однак краще про всяк випадок зробити резервну копію перед восстаноленіем файлів.

Є ще один формат виведення відмінностей командою diff: уніфікований формат. Він більш компактний, тому що містить зменшені контекстні рядки. Однак він підтримується тільки GNU diff і patch. Якщо ви його використовуєте, ви повинні бути впевнені, що у користувачів, для яких патч призначений, GNU patch. Linux допускає використання цього формату.

Уніфікований формат схожий на контекстний, але це не одне і теж. Патч в уніфікованому форматі можна створити так:

Результат буде седующій:

Як ви бачите, номери рядків укладені між "@". Крім того, є додатковий пробіл після "+" або "-". Це економить кілька байт. Інша відмінність: в уніфікованому форматі немає спеціального знака для заміни. Він просто видаляє старі рядки ( "-") і додає нові ( "+"). Різниця між цими діями полягає в тому, що при заміні використовується один і той же номер рядка, а при видаленні і додаванні різні.

Читаючи про три різних формати, ви ймовірно задумалися: а який же вибрати? Ось невелике порівняння:

Нормальний формат найбільш сумісний. Будь-які команди схожі на diff / patch повинні зрозуміти його. Його недолік - це відсутність контексту.

Контекстний формат широко поширений, але не всі команди його розуміють. Його перевага в наявності контексту.

Уніфікований формат теж включає контекст, і при цьому більш компактний. Але його підтримує лише GNU diff and patch.

Якщо ви впевнені, що патч буду використовувати тільки користувачі з GNU diff / patch, то найкраще вибрати уніфікований формат, так як він більш компактний. У більшості інших випадків кращий вибір - це контекстний формат. Нормальний формат слід використовувати якщо ви впевнені, що користувач буде застосовувати патч командами, які не підтримують контекстний формат.

Зміна кількості контекстних рядків

Можна змусити команду diff включати в патч сеньшее кількість рядків контексту, ніж повинно бути. У великих патчах це може сільон зменшити його розмір. Однак якщо зменшити кількість контекстних рядків, це може привести до непрацездатності патча. Цитати з довідки GNU diff: "Для більшості операцій в патчі має бути хоча б два рядки контексту."

Вказати кількість контестном рядків можна декількома способами:

Якщо ви хотіт використовувати контекстний формат, ви можете ви можете поєднати ці вказівки, додавши в опцію "-C". приклад:

] $ Diff -C 2 originaldirectory / updateddirectory /

Попередня команда буде використовувати контекстний формат з двома контекстними рядками.

Якщо ви хотіт використовувати контекстний формат, ви можете ви можете поєднати ці вказівки, додавши в опцію "-U". приклад:

] $ Diff -U 2 originaldirectory / updateddirectory /

Попередня команда буде використовувати уніфікований формат з двома контекстними рядками.

Якщо не вказувати який формат ви хочете використовувати, то команда буде виглядати приблизно так:

] $ Diff -2 originaldirectory / updateddirectory /

Однак це буде працювати тільки якщо ви визначите формат. Вам необхідно використовувати цю опцію або з "-c" або з "u".