Команда diff

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

Базовый синтаксис команды выглядит следующим образом:

$ diff [параметры] файл1 файл2

Утилита поддерживает огромное количество параметров и может выводить информацию о различиях файлов в нескольких различных форматах. Рассмотрим лишь наиболее часто используемые из них. Параметр -q позволяет выводить лишь информацию о том, что файлы различаются без вывода информации о самих различиях. Параметр -s сообщает утилите о том, что нужно выводить информацию и о том, что файлы идентичны. Параметр -y позволяет выводить содержимое двух файлов в двух столбцах с указанием на их различия. В случае получения плохо читаемого вывода он может комбинироваться с параметром --width=<количество символов>, позволяющим указать максимальное количество символов в каждом из столбцов. Для игнорирования регистра символов в файлах при сравнении может использоваться параметр -i, для игнорирования символов пробелов в конце файлов — параметр -Z, для игнорирования всех пробелов в файлах — параметр -w, для игнорирования всех пустых строк в файлах — параметр -B. При работе с файлами исходного кода следует использовать параметр -u, активирующий унифицированный формат описания различий и параметр -p, включающий в вывод имена различающихся функций. В случае возникновения необходимости в сравнении множества файлов вместо имен файлов могут использоваться имена директорий, а к списку параметров должен быть добавлен параметр -r, активирующий механизм рекурсивного обхода, а также параметр -N, позволяющий рассматривать отсутствующий файл как файл без какого-либо содержимого.

Примеры использования

Сравнение двух текстовых файлов

Для начала рассмотрим содержимое двух текстовых файлов, которые будут сравниваться.

Это содержимое файла sample1.txt:

It is a truth universally acknowledged, that a single man in possession
of a good fortune, must be in want of a wife.

However little known the feelings or views of such a man may be on his
first entering a neighbourhood, this truth is so well fixed in the minds
of the surrounding families, that he is considered the rightful property
of some one or other of their daughters.

А это — содержимое файла sample2.txt:

However little known the feelings or views of such a man may be on his
first entering a neighbourhood, this truth is so well fixed in the minds
of the surrounding families, that he is considered the rightful property
of some one or other of their daughters.

"My dear Mr. Bennet," said his lady to him one day, "have you heard that
Netherfield Park is let at last?"

Mr. Bennet replied that he had not.

Для сравнения этих двух файлов достаточно выполнить команду:

$ diff sample1.txt sample2.txt

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

1,3d0
< It is a truth universally acknowledged, that a single man in possession
< of a good fortune, must be in want of a wife.
<
7a5,9
>
> "My dear Mr. Bennet," said his lady to him one day, "have you heard that
> Netherfield Park is let at last?"
>
> Mr. Bennet replied that he had not.

Несложно заметить, что вывод в данном формате плохо читается, так как предназначен для программной обработки. Служебные метки имеют формат [номер строки или диапазон строк первого файла][действие][номер строки или диапазон строк второго файла]. Для описания действий используются буквенные символы, а именно, a — добавление, c — изменение и d — удаление. В данном случае метка 1,3d0 обозначает, что строки с первой по третью должны быть удалены из начала первого файла, а метка 7a5,9 — что после седьмой строки в первый файл должны быть добавлены строки с пятой по девятую из второго файла. Добавляемые и удаляемые строки обозначаются с помощью символов > и < соответственно.

Для вывода информации о различиях в унифицированном формате достаточно выполнить следующую команду:

$ diff -u sample1.txt sample2.txt

В результате будет получен следующий вывод:

--- sample1.txt    2017-07-23 18:47:05.467844761 +0300
+++ sample2.txt    2017-07-23 18:47:31.032479899 +0300
@@ -1,7 +1,9 @@
-It is a truth universally acknowledged, that a single man in possession
-of a good fortune, must be in want of a wife.
-
 However little known the feelings or views of such a man may be on his
 first entering a neighbourhood, this truth is so well fixed in the minds
 of the surrounding families, that he is considered the rightful property
 of some one or other of their daughters.
+
+"My dear Mr. Bennet," said his lady to him one day, "have you heard that
+Netherfield Park is let at last?"
+
+Mr. Bennet replied that he had not.

Очевидно, что данный формат кардинально отличается от рассмотренного выше. Список различий файлов начинается с двухстрочного заголовка, содержащего информацию о сравниваемых файлах (а именно, их имена и метки времени создания). С помощью служебных меток обозначаются фрагменты изменений, причем сами метки имеют формат @@ -диапазон-строк-первого-файла +диапазон-строк-второго-файла @@. В данном случае метка обозначает, что следует обработать семь строк первого файла начиная с первой строки и девять строк второго файла также начиная с первой строки. Добавляемые и удаляемые строки обозначаются с помощью символов + и - соответственно.

Сравнение файлов исходного кода

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

Это содержимое файла sample1.с:

  1. #include <stdio.h>
  2.  
  3. void say_hello(void)
  4. {
  5.         printf("Hello, Alex\n");
  6. }
  7.  
  8. int main (int argc, char** argv)
  9. {
  10.         say_hello();
  11.        
  12.         return 0;
  13. }

А это — содержимое файла sample2.с:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. void say_hello(void)
  5. {
  6.         char *username;
  7.  
  8.         username = getenv("USERNAME");
  9.  
  10.         if (username != NULL) {
  11.                 printf("Hello, %s\n", username);
  12.         } else {
  13.                 printf("Hello, Alex\n");
  14.         }
  15. }
  16.  
  17. int main (int argc, char** argv)
  18. {
  19.         say_hello();
  20.        
  21.         return 0;
  22. }

Для сравнения этих двух файлов достаточно выполнить команду:

$ diff -up sample1.c sample2.c > sample.patch

В результате будет создан файл с именем sample.patch и со следующим содержимым:

  1. --- sample1.c   2017-07-23 21:25:49.021449515 +0300
  2. +++ sample2.c   2017-07-23 21:29:00.664709854 +0300
  3. @@ -1,8 +1,17 @@
  4.  #include <stdio.h>
  5. +#include <stdlib.h>
  6.  
  7.  void say_hello(void)
  8.  {
  9. -       printf("Hello, Alex\n");
  10. +       char *username;
  11. +
  12. +       username = getenv("USERNAME");
  13. +
  14. +       if (username != NULL) {
  15. +               printf("Hello, %s\n", username);
  16. +       } else {
  17. +               printf("Hello, Alex\n");
  18. +       }
  19.  }
  20.  
  21.  int main (int argc, char** argv)

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

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

$ diff -uprN sample1 sample2 > sample.patch

В этом случае также будет создан файл с информацией об изменениях кода в соответствующих файлах из двух директорий с именами sample1 и sample2. При этом будет осуществлен рекурсивный обход директорий, причем непарные файлы не будут пропущены.