2017-02-21 12 views
4

Есть ли способ в Linux, используя , для создания diff/patch из двух файлов, хранящихся в памяти, с использованием общего формата (то есть: унифицированный diff , например, с помощью командной строки diff)?Diff/сравнить два файла по файловому дескриптору (fd) вместо имени файла

Я работаю над системой, в которой я создаю два текстовых файла в памяти, и никакое внешнее хранилище не доступно или не требуется. Мне нужно создать линейный diff двух файлов, а так как они mmap 'ed, у них нет имен файлов, что мешает мне просто позвонить system("diff file1.txt file2.txt").

У меня есть файловые дескрипторы (fd), доступные для использования, и это моя единственная точка входа в данные. Есть ли способ создать diff/patch, сравнив два открытых файла? Если реализация является лицензией MIT/BSD (т. Е. Не-GPL), тем лучше.

Спасибо.

+0

Я не могу найти способ вызвать 'diff' с 2 стандартными входными аргументами в качестве файлов, но это будет способ сделать это. –

+0

другим способом было бы использовать «comm - -» и линии подачи альтернативно, но он работает только в том случае, если файлы синхронизированы. –

+0

Я знаю, что вы сказали: «Внешнее хранилище не доступно или не требуется».Но считаете ли вы использование простой файловой системы в памяти, такой как ramfs? В основном это все дистрибутивы linux и AFAIK, их основное использование - выпустить временную файловую систему во время ранней загрузки. –

ответ

2

Учитывая требования, наилучшим вариантом было бы реализовать собственную память diff -au. Вы могли бы адаптировать соответствующие части OpenBSD's diff к вашим потребностям.


Вот набросок одного, как вы можете использовать команду /usr/bin/diff через трубу, чтобы получить объединённые различия между двумя строками, хранящихся в памяти:

  1. Создайте три трубы: I1, I2, и O.

  2. Вилка детского процесса.

  3. В процессе ребенка:

    1. переместить чтения концов труб I1 и I2 дескрипторам 3 и 4, а конец записи трубы вывод в дескриптор 1.

    2. Закройте другие концы этих труб в дочернем процессе. Откройте дескриптор 0 для чтения из/dev/null и дескриптор 2 для записи в/dev/null.

    3. Execute execl("/usr/bin/diff", "diff", "-au", "/proc/self/fd/3", "/proc/self/fd/4", NULL);

      Это выполняет diff двоичный в дочернем процессе. Он будет считывать входы от двух трубок, I1 и I2 и выводить разности в трубу O.

  4. Родительский процесс закрывает чтения концов I1 и I2 труб, и конец записи в вывод трубы.

  5. Родительский процесс записывает данные сравнения с концами записи из I1 и I2 труб, и считывает отличия от конца чтения вывода трубы.

    Обратите внимание, что родительский процесс должен использовать select() или poll() или аналогичный метод (желательно с неблокирующими дескрипторами), чтобы избежать взаимоблокировки. (Тупик возникает, если оба родителя и ребенка пытаются прочитать одновременно или писать одновременно.) Обычно родительский процесс должен избегать блокировки любой ценой, поскольку это может привести к тупиковой ситуации.

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

    Когда родительский процесс уведомление, что больше нет данных доступны в выводе трубы (read() возвращая 0), либо он уже закрыл концы записи для труб, либо произошла ошибка. Если ошибок нет, передача данных завершена, и дочерний процесс может быть получен.

  6. Родительский процесс пожинает ребенка, используя, например, waitpid(). Обратите внимание, что если есть какое-либо различие, diff возвращает со статусом выхода 1.

Вы можете использовать четвертую трубу, чтобы получить стандартный поток ошибок дочернего процесса; diff обычно не выводит ничего на стандартную ошибку.

Вы можете использовать пятую трубу, для записи execl() ошибок указать O_CLOEXEC с fcntl(). O_CLOEXEC означает, что дескриптор закрыт при выполнении другого двоичного файла, поэтому родительский процесс может обнаружить успешный запуск команды diff путем обнаружения конца данных в считываемом конце (read(), возвращающего 0). Если execl() терпит неудачу, ребенок может, например, напишите значение errno (в виде десятичного числа или как int) на этот канал, чтобы родительский процесс мог прочитать точную причину сбоя.

В целом, полный метод (который регистрирует стандартную ошибку и обнаруживает ошибки exec) использует 10 дескрипторов. Это не должно быть проблемой в обычном приложении, но может быть важным - например, рассмотреть сервер, обращенный к Интернету, с дескрипторами, используемыми входящими соединениями.

4

В Linux вы можете использовать файловую систему/dev/fd/pseudo (символическая ссылка на/proc/self/fd). Используйте snprintf(), чтобы построить путь для обоих дескрипторов файлов, таких как snprintf(path1, PATH_MAX, "/dev/fd/%d", fd1);, а также для fd2 и запустить diff.

Смежные вопросы