Каталог расширений

Популярные теги

3gp       avi       fb2       jpg       mp3       pdf      

Как создать патч файл


Как: Создать Патч С Помощью Команды Diff - Linux

Если вы внесли какие-то изменения в код и теперь хотите поделиться ними с остальными, самый лучший способ — это представить их в виде патча.

diff — это утилита командной строки в Linux, которая используется для создания патчей и которая предполагает что у вас в наличие есть две копии кода: одна копия — с вашими изменениями и вторая — без (оригинал).

В данной статье объясняется как создать патч для отдельно взятого файла или для целой директории с помощью команды diff и как применять этот патч после.

Создать Патч для Одного Файла в Linux

Допустим у вас есть оригинал какого-то файла.

Вы внесли в него какие-то изменения и сохранили результат, как новый файл.

Дельный Совет: Подзабыли значение какого-то термина в Git? Не беда! Просто прочитайте и добавьте в закладки эту статью! Эта статья →

Чтобы создать патч содержащий внесенные вами изменения, выполните следующую команду:

$ diff -u OriginalFile NewFile > PatchFile
Опция Описание
-u Создать diff файл в унифицированном (едином) формате

Применить Патч к Файлу

Человек, у которого есть оригинал файла и который хочет применить сделанные вами изменения, может пропатчить оригинальный файла с помощью следующей команды:

$ patch OriginalFile < PatchFile

Теперь содержимое оригинального файла идентично обновленному.

Отменить Патч

Чтобы откатить внесенные патчем изменения, используйте следующую команду:

$ patch -R OriginalFile < PatchFile
Опция Описание
-R, —reverse Отменить изменения

Создать Патч для Директории в Linux

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

Вы рекурсивно скопировали ее содержимое в новую директорию и внесли туда какие-то изменения.

Чтобы создать патч содержащий внесенные вами изменения, выполните следующую команду:

$ diff -ruN OriginalDir NewDir > PatchFile
Опция Описание
-r Применять diff рекурсивно ко всем общим подкаталогам
-u Создать diff файл в унифицированном (едином) формате
-N Считать отсутствующие файлы пустыми

Применить Патч к Директории

Человек, у которого есть оригинальная директории и который хочет применить сделанные вами изменения, должен куда-либо скопировать эту директорию вместе с патчем (например в /tmp), прейти туда и применить патч с помощью следующей команды:

$ patch -p0 < PatchFile
Опция Описание
-p0 Применить патч к той-же структуре каталога, которая была при его создании

Теперь содержимое оригинальной директории идентично содержимому обновленной директории.

Отменить Патч

Дельный Совет: Проблема с кодировкой файла? Узнай как пофиксить! Читать далее →

Чтобы откатить внесенные патчем изменения, используйте следующую команду:

$ patch -R -p0 OriginalFile < PatchFile
Опция Описание
-R, —reverse Отменить изменения

Yellow Leaf - Статьи - Использование комманд diff и patch

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

В этой статье используются без объяснения некоторые базовые команды Linux, такие как смена смена директории, копирование файлов и редактирование текстовых файлов.

Использование diff для создания простого патча

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


 [rechosen@localhost ~]$ diff originalfile updatedfile
 

Конечно, надо заменить originalfile и updatedfile соответствующими именами файлов. В результате должно получиться что-то вроде этого:


 1c1
 < These are a few words.
 \ No newline at end of file
 ---
 > These still are just a few words.
 \ No newline at end of file
 

Обратите внимание: Что бы продемонстрировать создание простого патча, я использовал оригинальный файл, содержащий строку "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:


 1c1
 < These are a few words.
 ---
 > These still are just a few words.
 

Как вы возможно заметили, я не объяснил что означают 3 знака "-". Они означают конец строк, которые надо заменить и начало строк на которые надо заменить. Разделение старых и новых строк. Вы увидите это знак только при замене ("c" между номерами строк).

Если мы хотим создать патч, мы должны поместить вывод команды diff в файл. Конечно это можно сделать, скопировав его из консоли и вставив в вашем любимом текстовом редакторе, а затем сохранив этот файл, но есть способ проще. Мы можем с помощью bash направить вывод команды diff в текстовый файл:


 [rechosen@localhost ~]$ diff originalfile updatedfile > patchfile.patch
 

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

Применение простого патча, который мы создали

Мы можем использовать патч, который только что создали, чтобы получить из оригинального файла обновленный. Для этого надо скопировать оригинальный файл и патч в одно и тоже место. И затем применить патч:


 [rechosen@localhost ~]$ patch originalfile -i patchfile.patch -o updatedfile
 

Естественно, и здесь надо изменить имена файлов на необходимые. Если все прошло хорошо, должен получиться файл, идентичный обновленному. Вы можете убедиться в этом, используя команду diff с опцией "-s":


 [rechosen@localhost ~]$ diff -s updatedfile [/path/to/the/original/updatedfile]/updatefile
 

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

Поздравляю! Если diff сообщила, что файлы идентичные, вы только что успешно создали и применили патч! Однако формат патча, который мы только что использовали не единственный. В следующей главе мы рассмотрим другой формат патча.

Контекстный патч

В первой главе мы создали патч, используя нормальный формат команды diff. Однако этот формат не обеспечивает контекстной зависимости, а использует строки целиком. Создадим патч для того же файла, но используя контектсный формат:


 [rechosen@localhost ~]$ diff -c originalfile updatedfile
 

Результат получится следующий:


 *** originalfile 2007-02-03 22:15:48.000000000 0100
 --- updatedfile 2007-02-03 22:15:56.000000000 0100
 ***************
 *** 1 ****
 ! These are a few words.
 --- 1 ----
 ! These still are just a few words.
 

Как вы видите, здесь включено имя файла. Это значит, что нам не придется набирать его во время применения патча. Далее идет дата и время последнего изменения файла. строка с 15 "*" показывает начало изменений. Они показывают, что надо сделать со следующим блоком текста. Два номера 1 - это номера строк (здесь тоже может быть сразу несколько строк), а "!" означает, что строки надо заменить. Строка с "!" перед тремя "-" должна быть заменена второй строкой с "!", которая идет после трех "-"(конечно сам ! не будет включен; это синтаксис контекстного формата). Как вы можете видеть, здесь нет знаков "c", "a" и "d".Действие, которое нужно сделать, определяется символом в начале строки. "!" означает замену. Другие символы - "+", "-" и " " (пробел). "+" означает добавление, "-" означает удаление, а " " означает ничего не делать: патч использует его чтобы убедиться, что он изменяет правильную часть файла.

Применять этот патч легче: при тех же условиях, что и для предыдущего патча (записываем вывод команды diff в файл, затем копируем патч и оригинал в одно и то же место), надо выполнить следующую команду:


 [rechosen@localhost ~]$ patch -i patchfile.patch -o updatedfile
 

Вы возможно сейчас думаете: зачем нам надо указывать имя нового файла? Это надо сделать из-за того, что патч старается изменить существующий файл, а не создает новый. Это удобно при создании патча для нескольких файлов сразу. Это приводит нас к следующей цели: создание патча для дерева файлов. Рассмотрим это в следующей главе.

Получение различий между несколькими файлами

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


 [rechosen@localhost ~]$ diff originaldirectory/ updateddirectory/
 

Обратите внимание: Если в директория есть поддиректории, то надо использовать опцию "-r".

В результате должно получится что-то вроде этого:


 diff originaldirectory/file1 updateddirectory/file1
1c1 < This is the first original file. --- > This is the first updated file. diff originaldirectory/file2 updateddirectory/file2 1c1 < This is the second original file. --- > This is the second updated file. 14d13 < We're going to add something in this file and to delete this line. 26a26 > This is line has been added to this updated file.

Обратите внимание: Я создал несколько несколько файлов для примера. Вы можете скачать архив, содержащий эти файлы: http://www.linuxtutorialblog.com/resource/uploads/diffpatchexamplefiles.tar.gz.

Как вы видите, нормальный формат содержит только имена файлов и изменяемые строки.

Теперь используем контекстный формат:


 diff -c originaldirectory/file1 updateddirectory/file1
 *** originaldirectory/file1 2007-02-04 16:17:57.000000000 +0100
 --- updateddirectory/file1 2007-02-04 16:18:33.000000000 +0100
 ***************
 *** 1 ****
 ! This is the first original file.
 --- 1 ----
 ! This is the first updated file.
 diff -c originaldirectory/file2 updateddirectory/file2
 *** originaldirectory/file2 2007-02-04 16:19:37.000000000 +0100
 --- updateddirectory/file2 2007-02-04 16:20:08.000000000 +0100
 ***************
 *** 1,4 ****
 ! This is the second original file.
 
 S
 O
 --- 1,4 ----
 ! This is the second updated file.
 
 S
 O
 ***************
 *** 11,17 ****
 C
 E
 
 - We're going to add something in this file and to delete this line.
 
 S
 O
 --- 11,16 ----
 ***************
 *** 24,28 ****
 --- 23,28 ----
 C
 E
 
 + This is line has been added to this updated file.
 
 Something will be added above this line.
 

Первая вещь, которую вы должны были заметить - это увеличение размера; контекстный формат содержит больше информации, чем нормальный. Этого не было заметно в первом премере, так как не было контекста. Однако теперь контекст есть, и за счет него размер патча увеличился. Кроме того, вы наверное заметили, что имя файла повторяется дважды. Это возможно сделано для того, чтобы легче было понять когда начался патч следующего файла или для обеспечения лучшего восстановления.

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

Создать патч было легко, но использование директорий ставит следующую проблему: бедут ли патч изменять только соответствующие файлы в текущей директории, или будет использовать соответствующий путь, указанный в файле? Чтобы узнать это, смотрите следующую главу!

Применение патча к нескольким файлам

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


 [rechosen@localhost ~]$ diff -c originaldirectory/ updateddirectory/ > patchfile.patch
 

Обратите внимание: мы используем контекстный формат патча, так как это является хорошим тоном.

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


 [rechosen@localhost ~]$ patch -i patchfile.patch
 

Однако возникает ошибка, что невозможно найти файлы для патча. Команда пытается найти файл file1 в текущей директории (по умолчанию патч убирает все пути перед именем файла). И конено файла нет, так как мы пытаемся обновить файлы в директории originaldirectory. Поэтому мы должны заставить патч использовать полный путь. Это делается следующим образом:


 [rechosen@localhost ~]$ patch -p0 -i patchfile.patch
 

Обратите внимание: Вы может подумать, что можно просто переместиться в originaldirectory и запустить патч. Но это не так! Так делать не стоит: если в в патче содержатся поддиректории, то он будет искать их в рабочей директории, и не найдет, или найдет не те. Используйте опцию "-p", чтобы заставить патч искать файлы в поддиректориях.

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


 (...)
 *** /home/username/sources/program/originaldirectory/file1 2007-02-04 16:17:57.000000000 +0100
 --- /home/username/sources/program/updateddirectory/file1 2007-02-04 16:18:33.000000000 +0100
 (...)
 

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

Восстановление оригинального файла из пропатченного

Иногда возникает необходимость восстановить оригинальный файл из пропатченного. Например, если в нем содержится ошибка. Для этого надо использовать опцию "-R":


 [rechosen@localhost ~]$ patch -p0 -R -i patchfile.patch
 

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

Унифицированный формат

Есть еще один формат вывода различий командой diff: унифицированный формат. Он более компактен, так как содержит уменьшенные контекстные строки. Однако он поддерживается только GNU diff и patch. Если вы его используете, вы должны быть уверены, что у пользователей, для которых патч предназначен, GNU patch. Linux допускает использование этого формата.

Унифицированный формат похож на контекстный, но это не одно и тоже. Патч в унифицированном формате можно создать так:


 [rechosen@localhost ~]$ diff -u originaldirectory/ updateddirectory/ 
 

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


 diff -u originaldirectory/file1 updateddirectory/file1
 --- originaldirectory/file1 2007-02-04 16:17:57.000000000 +0100
 +++ updateddirectory/file1 2007-02-04 16:18:33.000000000 +0100
 @@ -1 +1 @@
 -This is the first original file.
 +This is the first updated file.
 diff -u originaldirectory/file2 updateddirectory/file2
 --- originaldirectory/file2 2007-02-04 16:19:37.000000000 +0100
 +++ updateddirectory/file2 2007-02-04 16:20:08.000000000 +0100
 @@ -1,4 +1,4 @@
 -This is the second original file.
 +This is the second updated file.
 
 S
 O
 @@ -11,7 +11,6 @@
C E -We're going to add something in this file and to delete this line. S O @@ -24,5 +23,6 @@ C E +This is line has been added to this updated file. Something will be added above this line.

Как вы видите, номера строк заключены между "@". Кроме того, есть дополнительный пробел после "+" или "-". Это экономит несколько байт. Другое различие: в унифицированном формате нет специального знака для замены. Он просто удаляет старые строки ("-") и добавляет новые ("+"). Разница между этими действиями заключается в том, что при замене используется один и тот же номер строки, а при удалении и добавлении разные.

Сравнение форматов

Читая про три разных формата, вы вероятно задумались: а какой же выбрать? Вот небольшое сравнение:

  • Нормальный формат наиболее совместимый. Любые команды похожие на diff/patch должны понять его. Его недостаток - это отсутствие контекста.

  • Контекстный формат широко распространен, но не все команды его понимают. Его преимущество в наличии контекста.

  • Унифицированный формат тоже включает контекст, и при этом более компактем. Но его поддерживает только GNU diff and patch.

Если вы уверены, что патч буду использовать только пользователи с GNU diff/patch, то лучше всего выбрать унифицированный формат, так как он более компактный. В большинстве других случаев лучший выбор - это контекстный формат. Нормальный формат следует использовать если вы уверены, что пользователь будет применять патч командами, не поддерживающими контекстный формат.

Изменение количества контекстных строк

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

Указать количество контестных строк можно несколькими способами:

  • Если вы хотит использовать контекстный формат, вы можете вы можете совместить эти указания, добавив в опцию "-C". Пример:

    [rechosen@localhost ~]$ diff -C 2 originaldirectory/ updateddirectory/

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

  • Если вы хотит использовать контекстный формат, вы можете вы можете совместить эти указания, добавив в опцию "-U". Пример:

    [rechosen@localhost ~]$ diff -U 2 originaldirectory/ updateddirectory/

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

  • Если не указывать какой формат вы хотите использовать, то команда будет выглядеть примерно так:

    [rechosen@localhost ~]$ diff -2 originaldirectory/ updateddirectory/

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

Заключительные слова

Несмотря на то, что эта статья описывает множество особенностей работы команд diff и patch, она не может описать все их возможности. Если вы хотите узнать больше об этих командах, вы можете прочитать страницу помощи по этим командам и документацию GNU.

Источник

Использование комманд diff и patch [RTFM.WIKI]



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

В этой статье используются без объяснения некоторые базовые команды Linux, такие как смена смена директории, копирование файлов и редактирование текстовых файлов.

Использование diff для создания простого патча

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

[rechosen@localhost ~]$ diff originalfile updatedfile

Конечно, надо заменить originalfile и updatedfile соответствующими именами файлов. В результате должно получиться что-то вроде этого:

1c1 < These are a few words. \ No newline at end of file --- > These still are just a few words. \ No newline at end of file

Обратите внимание: Чтобы продемонстрировать создание простого патча, я использовал оригинальный файл, содержащий строку "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:

1c1 < These are a few words. --- > These still are just a few words.

Как вы возможно заметили, я не объяснил что означают 3 знака "-". Они означают конец строк, которые надо заменить и начало строк на которые надо заменить. Разделение старых и новых строк. Вы увидите это знак только при замене ("c" между номерами строк).

Если мы хотим создать патч, мы должны поместить вывод команды diff в файл. Конечно это можно сделать, скопировав его из консоли и вставив в вашем любимом текстовом редакторе, а затем сохранив этот файл, но есть способ проще. Мы можем с помощью bash направить вывод команды diff в текстовый файл:

[rechosen@localhost ~]$ diff originalfile updatedfile > patchfile.patch

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

Применение простого патча, который мы создали

Мы можем использовать патч, который только что создали, чтобы получить из оригинального файла обновленный. Для этого надо скопировать оригинальный файл и патч в одно и тоже место. И затем применить патч:

[rechosen@localhost ~]$ patch originalfile -i patchfile.patch -o updatedfile

Естественно, и здесь надо изменить имена файлов на необходимые. Если все прошло хорошо, должен получиться файл, идентичный обновленному. Вы можете убедиться в этом, используя команду diff с опцией “-s”:

[rechosen@localhost ~]$ diff -s updatedfile [/path/to/the/original/updatedfile]/updatefile

Замените текст между [ и ] на путь к оригинальному файлу. Например, если обновленный файл, который вы использовали при создании патча находится в родительской директории вышай текущей, то “[/path/to/the/original/updatedfile]” надо заменить на “..” (bash понимает это как родительскую директорию от текущей). И конечно надо изменить имена файлов на верные.

Поздравляю! Если diff сообщила, что файлы идентичные, вы только что успешно создали и применили патч! Однако формат патча, который мы только что использовали не единственный. В следующей главе мы рассмотрим другой формат патча.

Контекстный патч

В первой главе мы создали патч, используя нормальный формат команды diff. Однако этот формат не обеспечивает контекстной зависимости, а использует строки целиком. Создадим патч для того же файла, но используя контектсный формат:

[rechosen@localhost ~]$ diff -c originalfile updatedfile

Результат получится следующий:

*** originalfile 2007-02-03 22:15:48.000000000 0100 --- updatedfile 2007-02-03 22:15:56.000000000 0100 *************** *** 1 **** ! These are a few words. --- 1 ---- ! These still are just a few words.

Как вы видите, здесь включено имя файла. Это значит, что нам не придется набирать его во время применения патча. Далее идет дата и время последнего изменения файла. строка с 15 “*” показывает начало изменений. Они показывают, что надо сделать со следующим блоком текста. Два номера 1 - это номера строк (здесь тоже может быть сразу несколько строк), а “!” означает, что строки надо заменить. Строка с “!” перед тремя “-” должна быть заменена второй строкой с “!”, которая идет после трех “-”(конечно сам ! не будет включен; это синтаксис контекстного формата). Как вы можете видеть, здесь нет знаков “c”, “a” и “d”.Действие, которое нужно сделать, определяется символом в начале строки. “!” означает замену. Другие символы - “+”, “-” и ” ” (пробел). “+” означает добавление, “-” означает удаление, а ” ” означает ничего не делать: патч использует его чтобы убедиться, что он изменяет правильную часть файла.

Применять этот патч легче: при тех же условиях, что и для предыдущего патча (записываем вывод команды diff в файл, затем копируем патч и оригинал в одно и то же место), надо выполнить следующую команду:

[rechosen@localhost ~]$ patch -i patchfile.patch -o updatedfile

Вы возможно сейчас думаете: зачем нам надо указывать имя нового файла? Это надо сделать из-за того, что патч старается изменить существующий файл, а не создает новый. Это удобно при создании патча для нескольких файлов сразу. Это приводит нас к следующей цели: создание патча для дерева файлов. Рассмотрим это в следующей главе.

Получение различий между несколькими файлами

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

[rechosen@localhost ~]$ diff originaldirectory/ updateddirectory/


Обратите внимание: Если в директория есть поддиректории, то надо использовать опцию "-r".

В результате должно получится что-то вроде этого:

diff originaldirectory/file1 updateddirectory/file1 1c1 < This is the first original file. --- > This is the first updated file. diff originaldirectory/file2 updateddirectory/file2 1c1 < This is the second original file. --- > This is the second updated file. 14d13 < We're going to add something in this file and to delete this line. 26a26 > This is line has been added to this updated file.

Как вы видите, нормальный формат содержит только имена файлов и изменяемые строки.

Теперь используем контекстный формат:

diff -c originaldirectory/file1 updateddirectory/file1 *** originaldirectory/file1 2007-02-04 16:17:57.000000000 +0100 --- updateddirectory/file1 2007-02-04 16:18:33.000000000 +0100 *************** *** 1 **** ! This is the first original file. --- 1 ---- ! This is the first updated file. diff -c originaldirectory/file2 updateddirectory/file2 *** originaldirectory/file2 2007-02-04 16:19:37.000000000 +0100 --- updateddirectory/file2 2007-02-04 16:20:08.000000000 +0100 *************** *** 1,4 **** ! This is the second original file. S O --- 1,4 ---- ! This is the second updated file. S O *************** *** 11,17 **** C E - We're going to add something in this file and to delete this line. S O --- 11,16 ---- *************** *** 24,28 **** --- 23,28 ---- C E + This is line has been added to this updated file. Something will be added above this line.

Первая вещь, которую вы должны были заметить - это увеличение размера; контекстный формат содержит больше информации, чем нормальный. Этого не было заметно в первом премере, так как не было контекста. Однако теперь контекст есть, и за счет него размер патча увеличился. Кроме того, вы наверное заметили, что имя файла повторяется дважды. Это возможно сделано для того, чтобы легче было понять когда начался патч следующего файла или для обеспечения лучшего восстановления.

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

Создать патч было легко, но использование директорий ставит следующую проблему: бедут ли патч изменять только соответствующие файлы в текущей директории, или будет использовать соответствующий путь, указанный в файле? Чтобы узнать это, смотрите следующую главу!

Применение патча к нескольким файлам

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

[rechosen@localhost ~]$ diff -c originaldirectory/ updateddirectory/ > patchfile.patch


Обратите внимание: мы используем контекстный формат патча, так как это является хорошим тоном.

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

[rechosen@localhost ~]$ patch -i patchfile.patch

Однако возникает ошибка, что невозможно найти файлы для патча. Команда пытается найти файл file1 в текущей директории (по умолчанию патч убирает все пути перед именем файла). И конено файла нет, так как мы пытаемся обновить файлы в директории originaldirectory. Поэтому мы должны заставить патч использовать полный путь. Это делается следующим образом:

[rechosen@localhost ~]$ patch -p0 -i patchfile.patch

Обратите внимание: Вы может подумать, что можно просто переместиться в originaldirectory и запустить патч. Но это не так! Так делать не стоит: если в в патче содержатся поддиректории, то он будет искать их в рабочей директории, и не найдет, или найдет не те. Используйте опцию "-p", чтобы заставить патч искать файлы в поддиректориях.

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

(...) *** /home/username/sources/program/originaldirectory/file1 2007-02-04 16:17:57.000000000 +0100 --- /home/username/sources/program/updateddirectory/file1 2007-02-04 16:18:33.000000000 +0100 (...)

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

Восстановление оригинального файла из пропатченного

Иногда возникает необходимость восстановить оригинальный файл из пропатченного. Например, если в нем содержится ошибка. Для этого надо использовать опцию "-R":

[rechosen@localhost ~]$ patch -p0 -R -i patchfile.patch

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

Унифицированный формат

Есть еще один формат вывода различий командой diff: унифицированный формат. Он более компактен, так как содержит уменьшенные контекстные строки. Однако он поддерживается только GNU diff и patch. Если вы его используете, вы должны быть уверены, что у пользователей, для которых патч предназначен, GNU patch. Linux допускает использование этого формата.

Унифицированный формат похож на контекстный, но это не одно и тоже. Патч в унифицированном формате можно создать так:

[rechosen@localhost ~]$ diff -u originaldirectory/ updateddirectory/

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

diff -u originaldirectory/file1 updateddirectory/file1 --- originaldirectory/file1 2007-02-04 16:17:57.000000000 +0100 +++ updateddirectory/file1 2007-02-04 16:18:33.000000000 +0100 @@ -1 +1 @@ -This is the first original file. +This is the first updated file. diff -u originaldirectory/file2 updateddirectory/file2 --- originaldirectory/file2 2007-02-04 16:19:37.000000000 +0100 +++ updateddirectory/file2 2007-02-04 16:20:08.000000000 +0100 @@ -1,4 +1,4 @@ -This is the second original file. +This is the second updated file. S O @@ -11,7 +11,6 @@ C E -We're going to add something in this file and to delete this line. S O @@ -24,5 +23,6 @@ C E +This is line has been added to this updated file. Something will be added above this line.

Как вы видите, номера строк заключены между "@". Кроме того, есть дополнительный пробел после "+" или "-". Это экономит несколько байт. Другое различие: в унифицированном формате нет специального знака для замены. Он просто удаляет старые строки ("-") и добавляет новые ("+"). Разница между этими действиями заключается в том, что при замене используется один и тот же номер строки, а при удалении и добавлении разные.

Сравнение форматов

Читая про три разных формата, вы вероятно задумались: а какой же выбрать? Вот небольшое сравнение:

  • Нормальный формат наиболее совместимый. Любые команды похожие на diff/patch должны понять его. Его недостаток - это отсутствие контекста.

  • Контекстный формат широко распространен, но не все команды его понимают. Его преимущество в наличии контекста.

  • Унифицированный формат тоже включает контекст, и при этом более компактем. Но его поддерживает только GNU diff and patch.

Если вы уверены, что патч буду использовать только пользователи с GNU diff/patch, то лучше всего выбрать унифицированный формат, так как он более компактный. В большинстве других случаев лучший выбор - это контекстный формат. Нормальный формат следует использовать если вы уверены, что пользователь будет применять патч командами, не поддерживающими контекстный формат.

Изменение количества контекстных строк

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

Указать количество контестных строк можно несколькими способами:

  • Если вы хотите использовать контекстный формат, вы можете вы можете совместить эти указания, добавив в опцию "-C".

Пример:

[rechosen@localhost ~]$ diff -C 2 originaldirectory/ updateddirectory/

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

  • Если вы хотит использовать контекстный формат, вы можете вы можете совместить эти указания, добавив в опцию "-U".

Пример:

[rechosen@localhost ~]$ diff -U 2 originaldirectory/ updateddirectory/

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

[rechosen@localhost ~]$ diff -2 originaldirectory/ updateddirectory/

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

Заключительные слова

Несмотря на то, что эта статья описывает множество особенностей работы команд diff и patch, она не может описать все их возможности. Если вы хотите узнать больше об этих командах, вы можете прочитать страницу помощи по этим командам и документацию GNU.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.OKMore information about cookies

linux/diff_n_patch.txt · Последнее изменение: 2013/08/16 19:14 (внешнее изменение)

Создание patch’ей на Wix при помощи PatchWiz / Хабр


Добрый день всем! Хочется поделиться со всеми своим опытом создания системы для генерации патчей (да простит меня читатель за использование этого слова). Про wix довольно много было написано здесь и я предполагаю, что читатель немного знаком с ним, а вот проблему создания патчей как-то обошли. В нашей же компании они нашли широкое применение, в основном из-за своего размера, а также из-за возможности отката.

Для начала я опишу задачу, с которой я столкнулся, затем опишу базовую информацию для создания патчей. Также сделаю 2-ю часть с конкретным примером, утилитами для упрощения этого процесса и исходниками.
Задача:

1. Есть узкоспециализированный desktop продукт, который устанавливается по многим субъектам РФ в многие филиалы инстанции, связанной с лесным хозяйством. Давайте для удобство дадим имя этому условному продукту, допустим Ясень.
2. Для каждого субъекта собирается своя версия инсталляции, которые не отличаются бинарниками, а отличаются контент файлами, которые не меняются (карты, шаблоны отчетов, базовая БД и т.д.).
3. В «горячий» сезон обновления выпускаются раз в 1-2 недели (всем резко становится что-то нужно и часто что-то новое). При этом версия продукта меняется (либо major, minor или build)
4. В любой момент любой клиент может попросить как обновление, так и полную версию. Надо выйти из положения минимальными затратами.
5. ClickOnce не подходит, так как некоторые госструктуры, как ни странно, не имеют прямого выхода в интернет, либо он очень плох.
6. Сделать все на Wix, так как он бесплатен.

В этих условиях (пункт 4!), чтобы экономить трафик и не слать каждый раз по 100 Мб полной версии много раз лучше делать патчи.

Есть и пара прихотей разработчиков:
1. А также хочется свободы в пропусках патчей, то есть можно, например, обновляться через 1 или 2 обновления.
2. На схеме возможных обновлений эта прихоть выглядит так:


То есть, разрешены все варианты последовательности:
• Последовательно получать все патчи, потом получить полную msi (сплошная линия).
• Пропустить 2 патча, поставить 3-й, а потом поную msi (пунктирная).
• Поставить 1 патч, 1 пропустить, следующий поставить и получить полную версию msi.
• и т.д.
Давайте начнем с начала.

Разберемся версиями и с патчами

Как известно, версия в Wix состоит (=учитывает) из 3-х чисел: x.y.z, x — Major version, y — Minor version, z — Build number. Принцип изменения этих значений, на мой взгляд, определяется компанией, и какого-то явного правила нет, только — рекомендации.
В то же время, существует 3 типа обновлений:
  • Major update (правильнее — Major upgrade, но чтобы было единство давайте отставим major update):
    Major update это комплексное обновление продукта, которое затрагивает структуру устанавливаемых фич, меняет состав и название компонент и т.д. Major update удаляет предыдущую версию приложения и устанавливает новую.
  • Minor update (правильнее — Minor upgrade, но чтобы было единство давайте отставим minor update):
    Minor update затрагивает многие ресурсы инсталляции, однако ни одно из них не требует изменения ProductCode (об этом чуть ниже). Minor update может добавлять новые фичи или компоненты, но не может реорганизовывать дерево фич и компонентов, то есть не может удалять фичи и компоненты и перемещать компоненты из одной фичи в другую.
  • Small update:
    Small update это маленькое обновление которое затрагивает как правило несколько файлов, изменяя их содержимое.

«База» для обновлений в Wix

Реализации перечисленных вариантов обновления в Wix базируется на 2-х переменных:
1. Атрибут UpgradeCode элемента Product.
2. Атрибут ProductCode элемента Product.
Про Package.Id мы не говорим, так как он меняется почти всегда. Подробно о том, когда менять UpgradeCode и ProductCode, написано тут и тут.
Вкратце это так:
  • UpgradeCode продукта одного поколения обычно не меняется. Как только система переписана полностью, коренным образом, например, изменены применяемые технологии, то UpgradeCode меняют, отсекая все ранее сделанные пакеты обновления.
  • ProductCode меняется, если изменены имя msi пакета, удалены или изменены Component’ы. Если в новой версии просто поменялись уже существующие файлы или добавился новый Component, то ProductCode не меняют.

Пример

Пусть есть уже установленное приложение версии 1.0, мы создаем следующую версию инсталляции. Мы можем в ней поменять UpgradeCode и ProductCode на новое значение относительно предыдущей версии.
Давайте посмотрим, что получится, если мы поменяем\оставим старым значения этих атрибутов. Наш успех при попытке (1) создать пакет и (2) установить его отражен в таблице ниже:

Мы получим: * — minor update, ** — major update, *** — small update
Общий подход создания патча

Сама процедура создания патча в общем выглядит очень просто: есть одна или несколько «базовых» сборок и одна «конечная». Утилита генерации патчей создает пакет, который умеет обновлять продукт с версий, которые содержатся в «базовых» сборках, до версии, которая содержится в «конечной».

Как известно, Wix поддерживает 2 технологии создания патчей: используя PatchWiz.dll и сам Wix. Я не буду глубоко залезать в разборы всех достоинств и недостатков этих вариантов. Это не является целью статьи. Скажу лишь, что в результате проведенных опытов мы остановились на первом варианте (т.к. только на нем смогли получить удовлетворяющий нас результат).

Создание патча на Wix при помощи PatchWiz.dll

Для создания патча с использованием PatchWiz (будем использовать утилиту msimsp.exe) необходимо минимум 2 инсталляционных пакета (точнее 2 msi файла) и описатель патча (обычно файл называют Patch.wxs). Я не буду подробно описывать все возможности, которые им предусмотрены, иначе статья получится слишком большой, но основные моменты затрону. (Подробности можно накопать тут)

1. Сначала создается Patch.wxs, а в нем элемент PatchCreation.

<PatchCreation Id="{42D7EE3B-A712-4AD4-9B23-A8710FC486FA}" Codepage="1251" CleanWorkingFolder="yes" OutputPath="patch.pcp" WholeFilesOnly="yes"> 

Здесь:
Id – уникальный Id патча, всегда новый.
Codepage – кодовая страница для промежуточного (для нас) файла с расширение PCP.
CleanWorkingFolder – очищать временную папку после создания патча.
OutputPath – путь и имя промежуточного файла.
WholeFilesOnly – в патч будем включать изменившиеся файлы целиком, а не только изменившиеся блоки в них.

Внутрь PatchCreation записываются элементы с информацией о патче, здесь, я думаю, все понятно (PatchMetadata – не обязательный элемент).

<PatchInformation Description="Обновление Ясень" Comments="Обновление Ясень до 1.1" Manufacturer="Рога и копыта"/> <PatchMetadata AllowRemoval="yes" Description="Обновление Ясень" ManufacturerName="Рога и копыта" TargetProductName="Ясень" MoreInfoURL="http://рогаикопыта.рф/" Classification="Update" DisplayName="Обновление Ясень до версии 1.1"/> 

И, пожалуй, главное: указываем пути, где лежат базовые сборки и конечная.
<Family DiskId="2" Name="Yasen" SequenceStart="5000"> <UpgradeImage SourceFile="C:\Work\Yasen\v1.1\Setup.msi"> <TargetImage SourceFile="C:\Work\Yasen\v1.0.8\Setup.msi" Order="2"/> <TargetImage SourceFile="C:\Work\Yasen\v1.0\Setup.msi" Order="3"/> </UpgradeImage> </Family> 

Здесь:
DiskId – номер новой записи в таблице Media, не должен совпадать с Media Id базового установщика.
Name – имя линейки обновлений. Обновлять один продукт обновлениями с разными Family Name я не пробовал.
SequenceStart – номер для записи в таблице InstallExecuteSequence, не должен совпадать с существующими записями. В большинстве примеров, которые я видел, стоит 5000. Это значение не конфликтует со стандартными значениями Wix, в нашем пакете тоже это значение не используется. (Подробно про это есть в статьи про wix, перечисленные выше)

Элемент UpgradeImage – описывает конечную сборку.
SourсeFile – путь к конечной сборке (на самом деле не совсем на нее, а на ее «распакованную» версию, об этом написано ниже)
Id – идентификатор сборки.

Элемент TargetImage – описывает те сборки, которые могут быть обновлены текущим обновлением.
SourceFile – путь к базовой сборке.
Order – порядок базовых сборок (так и не понял, зачем это надо).
Id – идентификатор базовых сборок.

И в конце PatchCreation вставляется элемент PatchSequence

<PatchSequence PatchFamily= "Yasen" Sequence="1.1.0.0" Supersede="yes" ProductCode="{7381ABA7-774B-4D44-BD7B-0A90BBCF2B0A}" /> 

Здесь:
PatchFamily – указывает к какой линейке относится этот патч (где-то мы это видели уже?)
Sequence – указывает версию патча, чтобы отличать в каком порядке патчи были выпущены. Указывается в формате x.x.x.x.
Supersede – указывает, может ли этот патч отменять все предыдущие патчи (накопительный патч?)
ProductCode – код продукта (видимо, чтобы не ошибиться).
Сборка и установка

После того, как инсталляции и описатель патча готовы, надо сделать следующее:

1. Выполнить административную установку всех инсталляций в отдельные папки, например, так:

msiexec.exe /a 1.0\product.msi /qb TARGETDIR=C:\sample\1.0\admin msiexec.exe /a 1.1\product.msi /qb TARGETDIR=C:\sample\1.1\admin 

Обратите внимание, что путь к инсталляциям в PatchCreation должен указывать на эти «распакованные» версии, например, C:\sample\1.0\admin\product.msi

2. Компилируем Wix файлы:

candle.exe patch.wxs light.exe patch.wixobj -out patch.pcp 

3. Используем утилиту от PatchWiz:
msimsp.exe -s patch.pcp -p patch.msp -l patch.log 

И мы, наконец, получили желаемое!
Итог

Мы получили желаемый патч, однако для решения поставленной задачи нужно нечто большее:
  • Не хочется создавать каждый раз файл-описатель патча.
  • Эта система не очень эффективно работает, когда надо разрешить пропуск патчей. Причина в том, что мы указываем какие версии может патчить этот пакет, если мы хотим чтобы он мог патчить 5 версий, то объем может возрасти в 5 раз! Если мы указываем в качестве инсталяций для патча только предыдущую версию – экономим объем, но теряем возможность пропускать патчи.
  • Нужно автоматизировать этот процесс, чтобы не тратить много времени на сбор патчей.

Соответственно для решения этих неудобств я напишу вторую часть и опишу все, что обещал в начале поста.

Ссылки:
Wix tutorial (очень хорошее руковдоство)
Wix — Creating patches
MSDN — Patching and Upgrades

зачем нужны бинарные патчи, и как ими пользоваться — «Хакер»

Содержание статьи

В никсах не принято использовать бинарные патчи. Обычно обновления
распространяются в виде пакетов, тарболлов с исходниками или текстовых файлов,
созданных утилитой diff. Тем не менее, настроив свою систему на обновление с
помощью бинарных патчей, ты сможешь существенно сэкономить время и трафик и даже
отказаться от перезагрузки при обновлении ядра.

В этой статье мы рассмотрим четыре механизма создания
бинарных патчей и поговорим о назначении каждого из них.
В первой части статьи я расскажу о Ksplice — технологии,
способной довести uptime сервера до 100 %, далее мы поговорим об
инструментах xdelta и bsdiff, с помощью которых можно прилично
сэкономить на трафике при обновлении системы, в третьей части
уделим внимание механизму deltup, экономия трафика при использовании которого может достигнуть 95 %. Четвертая часть посвящена
фреймворку binpatchng, предназначенному для создания пакетов с
патчами для базовой инсталляции OpenBSD в домашних условиях.

 

Ksplice

Наверное, о Ksplice и его недавней покупке компанией Oracle слышали все. Это такой хитрый механизм (или даже, лучше сказать, хак),
который позволяет накатывать обновления на ядро Linux на лету, не
требуя перезагрузки машины или даже какой-то работы по сборке
модулей ядра.


Принцип работы Ksplice

Механизм Ksplice использует довольно интересный метод патчинга ядра, в основе которого лежит техника изменения компонентов
ядра прямо во время его работы. Делается это в два шага. На первом
этапе отрабатывает утилита генерации бинарного патча, которая
получает на вход месторасположение исходных текстов Linux-ядра и
файл, содержащий стандартный патч на исходный код в diff-формате.
Наложив этот патч на исходники, утилита компилирует ядро, сравнивает получившийся образ с ядром предыдущей версии и генерирует
модуль ядра, содержащий измененные части, а если конкретнее — код
измененных функций. Этот патч-модуль передается на машину, ядро
которой должно быть обновлено. На втором этапе в дело вступает
модуль ядра ksplice.ko, который вместе с патч-модулем загружается
в ядро целевой машины. После загрузки ksplice.ko анализирует патч-модуль на предмет наличия новых версий функций ядра и изменяет
адреса настоящих функций ядра, так чтобы они указывали на функции, содержащиеся в патч-модуле. Таким образом удается изменить
ядро, не перекомпилируя его.

Минус такого подхода в том, что фактически он подходит только
для исправления небольших ошибок и латания дыр, более крупные
обновления, затрагивающие множество функций, добавляющие
новый функционал и изменяющие внутренние структуры ядра, таким
образом не сделаешь. Тем не менее автор Ksplice отмечает достаточно высокую эффективность системы, она смогла в полностью автоматическом режиме сгенерировать 84 % патчей для bugfix-обновлений
ядра, выпущенных за три года. Для остальных обновлений пришлось
немного поработать руками.

К сожалению, в 2009 г., после открытия компании Ksplice Inc.,
автор решил закрыть код системы, не позволив частным лицам и сторонним компаниям самим создавать Ksplice-патчи. Поэтому в этой
статье мы не будем обсуждать эту тему и рассмотрим вопрос только
с клиентской точки зрения. А выглядит она так: сегодня Ksplice
принадлежит Oracle, которая продолжает предоставлять сервис
абсолютно бесплатного онлайн-обновления ядра для дистрибутивов
Ubuntu и Fedora, а также для собственного варианта RHEL под названием Unbreakable Linux. Пользователи всех остальных корпоративных дистрибутивов пролетают.

Теперь о том, как этим пользоваться. Для Ubuntu и Fedora на сайте
ksplice.com есть пакет, его следует скачать и установить
стандартными средствами:

$ sudo apt-get install curl
$ sudo dpkg -i ksplice-uptrack.deb

Далее соглашаемся с лицензией. Появится окно со списком всех
доступных для нашего ядра обновлений. Нажимаем кнопку Install, и
система скачает нужные патчи из Сети и загрузит их в ядро. Чтобы в
случае перезагрузки патч-модули были загружены, вместе с пакетом
устанавливается стартовый скрипт. Система всегда следит за обновлениям и сообщает о них с помощью значка в трее.

 

xdelta, bsdiff

Итак, с Ksplice и его извращенным подходом к накладыванию патчей
разобрались, теперь настало время поговорить об утилитах, которые
позволяют применить бинарные патчи на отдельно взятые приложения. В UNIX существует как минимум три таких инструмента:
это старейшая разработка xdelta, выросшая из куска кода rsync,
отвечающего за инкрементальный бэкап, его более современная и
развитая версия под названием xdelta3 и bsdiff, разработанные для
использования в BSD-системах.

По своей сути все три инструмента представляют собой аналог
всем нам известной программы diff, который оперирует не строками,
а последовательностью байт. Это дает возможность использовать
утилиты не только для вычисления разницы между файлами с
исходным кодом, но и для любых других данных, будь то бинарник
приложения, tar.gz-архив или даже видеофайл. Если тебе интересно,
как это работает, то рекомендую ознакомиться со статьей "Дельта-кодирование", опубликованной
в Wikipedia, я же лучше расскажу о том, зачем всё это
нужно и как с этим работать.

Для чего нужен бинарный diff? Всё очень просто: имея возможность накладывать бинарные патчи на уже работающие приложения
или целые архивы с исходниками, можно серьезно сэкономить на
трафике. В нашем безлимитном мире экономия трафика может и не
сделать большой выгоды в денежном плане, но вот время сократит существенно,
особенно при крупных обновлениях всей системы. Некоторые дистрибутивы позволяют
"встроить" утилиты для накладывания бинарных патчей в пакетный менеджер, так что, исправив
всего пару-тройку конфигов, можно просто сидеть и наслаждаться
быстрыми апдейтами.

Для того чтобы механизм инкрементальных апдейтов заработал,
должно быть выполнено три условия. Во-первых, пакетный менеджер дистрибутива должен поддерживать работу с утилитой инкрементального апдейта. Такая поддержка есть в pacman из ArchLinux
и через установку дополнительного пакета debdelta, в Debian.
Во-вторых, должен существовать специальный delta-репозиторий,
который будет отвечать за хранение патчей к пакетам и своевременно генерировать новые патчи для обновлений. Такие серверы обычно
держат энтузиасты, поэтому обычно их довольно трудно найти, а если
найдешь, никто не даст гарантии, что завтра сервер не исчезнет (это
главная беда бинарных патчей). И в-третьих, в кэше пакетного менеджера всегда должны лежать закэшированные во время предыдущей установки пакеты, на которые как раз и будут накладываться
патчи. Это важный момент, так как многие из нас (и я в том числе)
любят время от времени этот кэш подчищать (однако для debdelta, о
котором мы поговорим ниже, и они не нужны).

Теперь о том, как происходит настройка пакетного менеджера:

В ArchLinux всё решается довольно просто. Устанавливаем третью
версию xdelta:

$ sudo pacman -S xdelta3

Открываем /etc/pacman.conf, снимаем знак комментария со
строки UseDelta. Открываем список репозиториев /etc/pacman.d/mirrorlist и в самое его начало добавляем следующую строку:

Server = http://delta.archlinux.fr/$repo/os/$arch

Сохраняем файл и пробуем выполнить полное обновление дистрибутива:

$ sudo pacman -Syu

Разница должна быть, что называется, налицо. Проблема только в
том, что archlinux.fr содержит дельты далеко не для всех пакетов, но,
увы, альтернативы здесь просто нет.

В Debian это делается несколько иначе. Существует специальная
утилита (а точнее, набор утилит) под названием debdelta, которая
умеет разбирать deb-пакеты по косточкам, вычислять разницу между
файлами двух версий пакетов с помощью xdelta и генерировать
новый пакет на основании этих данных. При накатывании патчей это
чудо использует уже установленные в систему пакеты, а потому не
требует хранения старых версий пакетов в кэше apt-get. Проблема
только в том, что debdelta никак не интегрируется с apt-get, а поэтому
его надо вызывать вручную перед каждым обновлением системы:

$ sudo apt-get update
$ sudo debdelta-upgrade
$ sudo apt-get upgrade

Зато не нужно никакой настройки и возни с репозиториями
(официальный репозиторий успешно работает уже многие годы). Сам
debdelta устанавливается так:

$ sudo apt-get install debdelta

Стоит сказать о том, что BSD-аналог xdelta под названием
bsdiff также имеет большое применение. Он
был написан для утилиты обновления системы freebsd-update и стал
частью базовой установки FreeBSD в 2005 г. Каждый раз, когда ты делаешь "freebsd-update install", в дело вступает bsdiff (а точнее, его
часть bspatch), который прозрачно обновляет систему с помощью бинарных патчей. Благодаря BSD-лицензии, разрешающей включать
код в закрытые приложения, bsdiff получил большое распространение и за пределами BSD.

 

deltup

Интересную альтернативу бинарным патчам предложил в свое время
один из поклонников дистрибутива, Gentoo. Он создал утилиту
deltup, которая брала два архива с исходниками разных
версий приложения, распаковывала их, генерировала патч с помощью
стандартного diff, упаковывала его и снабжала информацией, нужной
для получения не отличимого от оригинала архива с одной версией
приложения из архива с другой. Говоря простым языком, deltup-файлом
можно пропатчить тарболл старой версии программы, чтобы получить
тарболл с ее новой версией, избежав необходимости в загрузке всего
тарболла. Результаты работы утилиты оказались просто поразительными: средний размер deltup-патча составляет всего 15 % от размера
оригинального архива, а зачастую и 5 % (с этими цифрами можно ознакомиться на
странице статистики головного deltup-сервера).

Сегодня поддержка deltup в Gentoo есть из коробки, также необходимый инструментарий был портирован во FreeBSD. Существует
несколько более или менее стабильно работающих серверов, отвечающих за генерацию и отдачу deltup-патчей. Настройка, опять же,
совсем не сложна:

В Gentoo порядок действий следующий. Устанавливаем инструменты deltup и getdelta:

$ sudo emerge deltup getdelta

Добавляем в /etc/make.conf следующую строку:

$ sudo vi /etc/make.conf
FETCHCOMMAND="/usr/bin/getdelta.sh \"\${URI}\" -O
\"\${DISTDIR}/\${FILE}\""

Так мы сообщим emerge о том, что хотим использовать команду
getdelta для получения архивов с исходниками. Далее открываем
конфигурационный файл /etc/deltup/getdelta.rc и пишем туда следующее:

$ sudo vi /etc/deltup/getdelta.rc
# Адрес локального репозитория (если есть)
LOCAL_MIRROR=1.2.3.4
# Максимальная позиция в очереди на ожидание дельты
MAXIMUM_ACCEPTABLE_QUEUEPOS=10
# Удалять старые версии файлов
REMOVE_OLD=yes

Опцию LOCAL_MIRROR можно не добавлять, она нужна только в том
случае, если в локалке есть Gentoo-репозиторий, который можно использовать вместо запроса патча от deltup-сервера. Опция MAXIMUM_ACCEPTABLE_QUEUEPOS задает максимальную позицию в очереди на
создание дельты. Большинство deltup-серверов генерируют дельты
во время первого обращения клиента за архивом, поэтому очередь за
особо тяжеловесными приложениями и последними обновлениями
может выстроиться большая. Нет каких-то определенных рекомендаций по поводу размера очереди, так как нагрузка на сервер может
быть разной и время, которое ты можешь прождать, — тоже. Самостоятельно указывать какой-либо deltup-сервер не требуется, сегодня все
серверы подключены к linux01.gwdg.de, который вписан в getdelta по
умолчанию.
Это всё, при следующем обновлении пакета ты должен заметить
разницу во времени скачивания.

Порт deltup есть и для FreeBSD. Он не требует своего собственного
репозитория и может использовать deltup-сервера Gentoo (все-таки
исходники приложения для разных платформ одни и те же). Чтобы
научить систему портов FreeBSD использовать deltup для обновления софта, необходимо сделать следующее:

1. Установить deltup и wget из портов:

$ cd /usr/ports/sysutils/deltup
$ sudo make install clean
$ cd /usr/ports/ftp/wget
$ sudo make install clean

2. Добавить в файл /etc/make.conf следующую строку:

$ sudo vi /etc/make.conf
FETCH_CMD=/usr/local/bin/getdelta.sh

Теперь независимо от того, используешь ли ты систему портов напрямую или различные фронт-энды типа portupgrade, обновления будут происходить с помощью deltup-сервера. Однако необходимо быть
готовым к тому, что иногда deltup ошибается и собирает не совсем
точную копию архива, которая вполне нормально распаковывается,
но имеет неправильную контрольную сумму (это следствие микроразличий в разных версиях gzip и bzip2). В этом случае сборку пакета
можно осуществить, добавив предписание NO_CHECKSUM:

$ cd /usr/ports/games/cowsay
$ make NO_CHECKSUM install clean

 

Шпаргалка по бинарным патчам

$ bsdiff старый_файл новый_файл файл_патча
$ bspatch старый_файл новый_файл файл_патча
$ xdelta3 -e -s старый_файл новый_файл файл_патча
$ xdelta3 -d -s старый_файл файл_патча новый_файл
$ deltup -mjb 9 старый_файл новый_файл файл_патча
$ deltup -p файл_патча
$ debdelta старый_файл новый_файл файл_патча
$ debpatch -A файл_патча / новый_файл

 

OpenBSD Binpatch

Еще один инструмент, о котором я бы хотел рассказать в этом обзоре,
не имеет отношения к бинарным патчам как таковым, но будет интересен и полезен владельцам компов, работающих под управлением
операционной системы OpenBSD.

Все, кто имеет хоть малейшее представление о процессе выпуска
релизов и заплаток для OpenBSD, знают, насколько он самобытен и
прост. При выпуске релизов разработчики скорее ориентируются на
план, чем на накопление достаточного количества важных изменений. После выпуска новой версии ОС сразу начинается работа над
следующей, а все ошибки и баги, найденные в это время, фиксятся с
помощью заплаток, которые каждый пользователь/админ должен
скачать, наложить на исходный код и пересобрать его. Делать это
не только жутко неудобно, но порой просто не представляется возможным. Многие железки под управлением OpenBSD не обладают
достаточной мощностью и дисковым пространством, для того чтобы
содержать в себе всё дерево исходных текстов OpenBSD, компилятор,
линковщик и промежуточные результаты компиляции. Но даже если
всё это на железке есть, но сама железка при этом не единственная
в подчинении, однотипные действия придется выполнять несколько
раз, что тоже не слишком интересно.

Чтобы решить эти проблемы,был придуман фреймворк
binpatch, позволяющий скачивать патчи, накладывать их на исходный код, собирать
пропатченное приложение или ядро и помещать его в архив в полуавтоматическом режиме. Позднее появилась модификация фреймворка под названием
binpatchng, которая
позволяла упаковывать пропатченные приложения не только в
архив, но и в пакет OpenBSD-формата, да так, что при удалении этого
пакета система откатывалась в первоначальное состояние.
Binpatchng полностью основан на Makefile’ах и концептуально
очень близок к системе портов. Чтобы создать новый патч, необходимо прописать в нужный Makefile пару простых правил, описывающих патч и способ его сборки, а затем выполнить команду make. Всё
остальное система возьмет на себя и вскоре сгенерирует готовый
к установке архив или пакет, который достаточно скопировать на
нужную машину и установить с помощью стандартных средств.

На
пальцах всё это выглядит следующим образом:

1. Скачиваем фреймворк и распаковываем его в каталог /usr
(на самом деле можно и в другое место):

$ cd /tmp; wget http://goo.gl/hvF7O
$ su
# tar -xzf /tmp/binpatchng-1.1.tar.gz -C /usr

2. Скачиваем архивы sys.tar.gz и src.tar.gz с официального FTP
и помещаем их в каталог distfiles, внутри binpatchng:

# cd /usr/binpatchng-1.1/
# mkdir distfi les
# cd distfi les
# wget ftp://ftp.openbsd.org/pub/OpenBSD/4.9/sys.tar.gz
# wget
ftp://ftp.openbsd.org/pub/OpenBSD/4.9/src.tar.gz

3. Скачиваем инсталляционные архивы для нужной архитектуры
(например ftp://ftp.openbsd.org/pub/OpenBSD/4.9/i386/) и помещаем
их в distfiles/имя_архитектуры.

4. Пишем Makefile. Для этого переходим на страницу
www.openbsd.org/errata.html, выбираем нужный релиз (для 4.9 пока патчей нет, поэтому я
возьму за пример 4.8), выбираем интересующий нас патч (например 001_bgpd.patch), открываем его. В первой строке будет указан способ сборки
патча. Наша задача — переложить его на язык Makefile. Это просто:
создаем файл /usr/binpatchng-1.1/Makefile и пишем в него следующее:

# vi /usr/binpatchng-1.1/Makefi le
# Для какой архитектуры собираем? (Можно не указывать,
# если совпадает с архитектурой текущей машины.)
ARCH=i386
# Здесь перечисляем патчи (просто откидываем расширение patch)
PATCH_COMMON=001_bgpd
# Здесь идут инструкции для сборки патча 001_bgpd.patch
001_bgpd:
cd ${WRKSRC}/usr.sbin/bgpd
(${_obj}; ${_depend}; ${_build})
# Далее можно поместить инструкции по сборке остальных патчей…


Правим binpatch Makefile

Самое сложное в этом файле — это инструкции сборки, они могут
показаться мудреными, однако написать их очень легко. Достаточно
просто перевести описанные в официальном патче инструкции на
макроопределения. Например, в нашем случае они выглядели так:

cd usr.sbin/bgpd
make obj
make depend
make
make install

Сравни их с директивами в Makefile, и всё поймешь. За дополнительными подробностями обращайся к примеру, описанному в файле
Makefile.sample. Теперь можно собрать пакет с нужным патчем:

# cd /usr/binpatchng-1.1/
# make PATCH="001" build
# make PATCH="001" plist
# make PATCH="001" package

Пакет с результатом должен появиться в каталоге patches, его следует
скопировать на нужную машину и установить с помощью такой команды:

# pkg_add binpatch-4.9-i386-001.tgz z

 

INFO

Debdelta имеет
опцию '--delta-algo',
с помощью которой
можно указать предпочитаемую утилиту для создания
патчей. Доступные
варианты: xdelta,
xdelta-bzip, xdelta3
и bsdiff.

Работу по написанию Makefile
для binpatchng
можно полностью
автоматизировать,
если воспользоваться скриптом,
опубликованным
в дискуссионном
листе OpenBSD.

Улучшенный 9-patch редактор / Блог компании ALEE Software / Хабр

Добрый день, Хабражитель!

Уверен с форматом nine-patch уже все давным-давно знакомы, вероятно даже не раз пользовались им в своих Android-проектах (а может даже и где-то ещё). Также о нём чуть менее года назад skkap писал небольшую статью о том, что это за формат, и «с чем его едят». Также он описал инструмент, предлагаемый для быстрого редактирования этих самых файлов.

Собственно, прошёл уже почти год с того момента и я, опять наткнувшись на ту статью и вспомнив жалобы знакомых на неудобство этого самого «инструмента», решил посмотреть нет ли каких других вариантов и, к моему большому удивлению, я таковых не нашёл. Вероятно потому, что формат сам из себя ничего сложного и специфичного не представляет (при желании nine-patch файлы можно редактировать почти в любом графическом редакторе поддерживающем png) и тем более уже есть готовый инструмент (пусть и не самый удобный).

Тут я и решил лично взяться за дело…

Первые наброски

Итак, за несколько дней на основе своей же разрабатываемой библиотеки мне удалось добиться достаточно неплохих, как мне кажется, результатов — ими бы я и хотел поделиться с вами. Пусть ещё не сглажены все углы, есть некоторые недочёты и, возможно, баги (ну а где их нет), но всё же…

Собственно, само небольшое приложение-редактор выглядит вот так:

Несколько преимуществ по сравнению с ранее упомянутой «древней» тулзой:

  • Возможность быстрого изменения nine-patch данных простым передвижением/изменением направляющих и областей
  • Отображение пиксельной линейки и расстояний между отдельными направляющими на области
  • Кастомизируемое превью итогового файла динамически обновляемое при редактировании
  • Распознавание nine-patch данных на изображениях не имеющих в имени ".9", а также возможность загрузки простых изображений для быстрой конвертации в nine-patch формат
  • Возможность загрузки любых форматов изображений, поддерживаемых в J2SE, помимо png
Это плюсы, касающиеся исключительно редактирования nine-patch файлов в готовом визуальном редакторе. Сам по себе запускаемый jar-файл приложения можно использовать в качестве подключаемой библиотеки для парсинга nine-patch изображений, встраивания редактора в свои собственные приложения на Java, стилизации компонентов и многого другого, но не буду распыляться — конкретно об этом я немного позже расскажу в отдельной статье в Java-блоге всем кто интересуется.
Немного описания...

Собственно, при работе с этим редактором вам придётся иметь дело с несколькими его частями…

Основной тулбар

Редактор

  • Для zoom'а области можно использовать сочетание Ctrl (или Alt) + Mouse wheel
  • Перемещая направляющие можно быстро редактировать области (в допустимых границах)
  • Перетаскивать области можно за чёрные линии по краям, либо за области внутри изображения
  • Добавлять новые растягиваемые области можно зажимая левую кнопку мыши слева или сверху от изображения
  • Стирать растягиваемые области можно зажимая правую кнопку мыши слева или сверху от изображения
  • Передвигать область (если появляется скролл) можно зажимая среднюю кнопку мыши (колесо)
Предпросмотр изображения

  • За сайзеры вокруг изображения можно изменить его размер
  • На верхнем тулбаре можно настроить отображаемый в изображении текст и иконку
  • На нижнем тулбаре можно настроить фон под изображением
  • Сжимает изображение до минимального размера с учётом содержимого
В общем и целом — это всё. Остальное вы можете и сами посмотреть/пощупать, запустив приложение у себя.

Кстати, по умолчанию на редактирование открывается вот это изображение:

Собственно, приложение:
Запускаемый jar

Естественно, для запуска вам потребуется установленная на вашей ОС jre.
Работать приложение должно на любой ОС, где доступны версии JDK 1.6.20+ (или OpenJDK).

В заключении

Надеюсь тем, кто часто работает с nine-patch файлами этот редактор поможет облегчить жизнь :)

Естественно, редактор будет и далее понемногу развиваться вместе с библиотекой, если будут появляться дельные предложения по его усовершенствованию.

Кстати о птицах — сама Web Look and Feel библиотека, а также данный редактор написанный на её основе распространяются под лицензией GPLv3 и имеют полностью открытый исходный код.

Так как более новая версия библиотеки, на которой основан данный редактор ещё не выпущена — загрузить исходники на данный момент можно напрямую отсюда:
http://weblookandfeel.com/downloads/ninepatch-sources.zip

А дистрибутив библиотеки без исходников отсюда:
http://weblookandfeel.com/downloads/ninepatch.jar

Также я рад буду выслушать любые комментарии и предложения и…
Спасибо за ваше внимание!

# Update

После нескольких комментариев, а также нахождения пары багов я внёс небольшие изменения в библиотеку и редактор, собственно что было изменено:
  • Объединённый с редактором предпросмотр итогового 9-patch изображения
  • Возможность выбора цвета текста в предпросмотре
  • Возможность копирования 9-patch данных из одного редактора в другой
  • Сохранение всех настроек редактора при закрытии
  • Возможность мультистрокового текста в превью ("\n" или ";" разделяют строки)
  • Привязка перемещения диалога превью к диалогу редактора
  • Добавлен слайдер и кнопки для zoom'а области на нижний тулбар
  • В имена файлов автоматически добавляется пометка ".9" при сохранении, если таковой нет
Также я в курсе последней оставшейся проблемы — некорректной работы выборщика файлов. Эта проблема будет устранена немного позднее, вместе с выходом очередной версии Web Look and Feel библиотеки.

Загрузить бинарники и исходники можно по старым ссылкам — они уже обновлены:
Запускаемый jar | Исходный код | Чистая библиотека

Как сделать батник

Обновлено: 07.10.2019, Computer Hope

Ниже приведены шаги по созданию командного файла на любом компьютере под управлением операционной системы Microsoft (например, MS-DOS или любой версии Windows).

Создание командного файла в Microsoft Windows

В Windows вы можете создать командный файл, выполнив следующие действия или шаги, указанные в разделе командной строки MS-DOS. Если вам удобнее работать с Microsoft Windows, вы можете использовать любой текстовый редактор (например, Блокнот или WordPad) для создания командных файлов.Если расширение файла заканчивается на .bat . В приведенном ниже примере мы используем Блокнот Windows для создания командного файла.

  1. Нажмите Пуск.
  2. Введите Блокнот в поле Выполнить и нажмите Введите .
  3. Открыв Блокнот, введите следующие строки или скопируйте и вставьте их.
 @ эхо выкл. echo Привет, это тестовый пакетный файл Пауза каталог c: \ windows пауза 
  1. Щелкните File , а затем Save , а затем перейдите туда, где вы хотите сохранить файл.В качестве имени файла введите test.bat и, если в вашей версии Windows есть параметр Сохранить как тип , выберите Все файлы , в противном случае он будет сохранен как текстовый файл. Выполнив эти шаги, нажмите кнопку Сохранить и выйдите из блокнота.
  2. Чтобы запустить командный файл, дважды щелкните его, как любую другую программу. После завершения работы командного файла он автоматически закрывается.

Создание командного файла в MS-DOS

Чтобы создать пакетный файл в MS-DOS или командной строке Windows, выполните следующие действия.

Заметка

Новые версии Windows или компьютеры с 64-разрядной версией Windows больше не поддерживают команду редактирования, что означает, что эти шаги больше не работают. Выполните указанные выше действия, если вы используете последнюю версию Windows.

  1. Откройте командную строку Windows.
  2. В командной строке MS-DOS введите: edit test.bat и нажмите Enter.
  3. При правильном вводе должен появиться синий экран редактирования. На экране редактирования введите:
 @ эхо выкл. echo Привет, это тестовый пакетный файл Пауза каталог c: \ windows пауза 
  1. После ввода этих трех строк щелкните File и выберите exit ; когда будет предложено сохранить, нажмите Да .Пользователи без мыши могут выполнить ту же задачу, нажав Alt + F для доступа к меню файла, а затем X для выхода. Нажмите Введите , чтобы сохранить изменения.
  2. Вернувшись в командную строку MS-DOS, введите: test и нажмите . Введите , чтобы запустить файл test.bat. Поскольку первая строка - это пауза, сначала вам будет предложено нажать клавишу. После этого командный файл запускается построчно; в этом случае перечисление файлов в каталогах Windows и Windows \ system.
Наконечник

Если вы хотите добавить больше строк в этот командный файл, введите edit test.bat , чтобы открыть файл для редактирования.

Дополнительная информация о команде редактирования MS-DOS находится на нашей странице команд редактирования. Некоторые версии MS-DOS и загрузочные дискеты могут не иметь команды редактирования. В этом случае вам нужно будет либо получить файл edit.com, либо использовать команду copy con.

.

Как создать командный файл для запуска cmd от имени администратора

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
.

Как создать пакетный файл для командной строки Visual Studio

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд
.Командная строка

- Как создать командный файл, который принимает два целых числа в качестве параметров

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
.

Смотрите также