У меня есть функция Bash для отображения man-страниц, отображаемых в виде postscript, в PDF:

function psman () {
    man -t "$@" | ps2pdf - /tmp/manpage.pdf
    evince /tmp/manpage.pdf
}

(Обновление: я избавился от периферийных сложностей, таких как динамическая генерация имени временного файла и использование nohup)

Это отлично работает. Для скриншота его использования, смотрите https://www.tartley.com/postscript-formatted-man-pages.

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

$ evince <(man -t ls | ps2pdf - -)

Это не работает Evince отображает ошибку в своем графическом интерфейсе:

Unable to open document "file:///dev/fd/63".
PDF document is damaged

Зачем? Как я могу генерировать и просматривать PDF без генерации промежуточных файлов?

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

Обновление: чтобы получить больше информации, я попытался заменить 'evince' на 'ls':

$ ls -l <(man -t ls | ps2pdf - -)
lr-x------. 1 jhartley jhartley 64 Aug 23 08:59 /dev/fd/63 -> pipe:[196475]

где дирколор красит:

  • /dev/fd/63 как ORPHAN (символическая ссылка, указывающая на несуществующий файл), и
  • pipe:[196475] как «MISSING» (несуществующий файл, на который указывает символическая ссылка)

Так может быть, evince просто дается ссылка, указывающая на файл, который не существует? Чтобы имитировать это, я создал символическую ссылку, которая указывает на несуществующий файл, а затем открыл его с помощью команды «evince». Но вместо сообщения «PDF поврежден», приведенного выше, появляется сообщение «Нет такого файла или каталога».

Обновление: я думаю, что типы файлов ORPHAN/MISSING - красная сельдь. Я вижу ту же символическую ссылку ORPHAN/MISSING, когда делаю очень простую подстановку процесса:

$ ls -l <( echo 123 )

и использование того же конвейера man|ps2pdt работает нормально, когда подстановка процесса подается в diff:

$ diff <(man -t ls | ps2pdf - - | tr "\0" "0") <(man -t ls | ps2pdf - - | tr "\0" "0")
248c248
< /ID [<95A81B38FAE8E6FE3C899586A1DEE861><95A81B38FAE8E6FE3C899586A1DEE861>]
---
> /ID [<2F9164BD9265C8540A4A8E7068076344><2F9164BD9265C8540A4A8E7068076344>]

(Здесь я добавил 'tr' в конвейеры, чтобы исключить ноль / ноль символов в выводе pdf, чтобы diff воспринимал файлы как текстовые, а не двоичные.)

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

3 ответа3

1

Просто предположение, но правдоподобное:

evince ищет через "файл", поток, который он получает, не доступен для поиска. Сравните Почему подстановка процесса BASH не работает с некоторыми командами?

Это означает, что это (почти?) невозможно получить то, что вы хотите без какого-либо промежуточного файла. Лучшее, что я могу придумать, это такой скрипт:

#!/bin/bash

tmpd="/dev/shm"

( tmpf="$(mktemp -p "$tmpd" "tmp [man $*] XXX.pdf")"
man -t "$@" | ps2pdf - > "$tmpf"
evince "$tmpf"
rm "$tmpf" ) 2>/dev/null &

Замечания, подводные камни и т.д .:

  1. Когда $tmpd равен /dev/shm , временный файл создается в памяти. Я предполагаю, что он настолько близок к «без создания каких-либо промежуточных файлов», насколько вы можете легко получить, сохраняя его доступным для поиска.
  2. Независимо от того, где он находится, мы должны удалить его потом. Если сценарий прерывается (например, с помощью Ctrl + C) между mktemp и rm , файл сохраняется, и мы не хотим его. Есть несколько подходов к этой проблеме, вы можете trap сигналы, если хотите; Я решил запустить всю последовательность в фоновом режиме ( ( … ) &), что может быть достаточно хорошо.
  3. Мой evince не откроет файл из /dev/shm если его имя не заканчивается на .pdf (это поведение не учитывает регистр). Вот почему в шаблоне имени файла есть .pdf . В /tmp такой проблемы нет. Зачем? Я не знаю.
  4. Шаблон имени файла создается с $* чтобы сделать его несколько осмысленным (он отображается в заголовке окна evince).
1

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

Теоретически вы можете прочитать (или mmap) весь файл в память, но это не сработает с действительно большими файлами, и PDF предназначен для того, чтобы справляться с действительно большими файлами (и, действительно, файлы PDF с качеством печати могут быть действительно большой). Таким образом, поиск является неотъемлемой частью использования файла PDF, и подстановка процесса не поддерживает поиск.

Есть другие приложения командной строки, которые нужно искать или думать, что они делают. (Иногда поиск - просто попытка программиста выяснить, насколько большой файл, для удобства.) Существуют другие форматы файлов, которые ставят индекс в конце (например, сжатие Zip) и действительно полагаются на поиск. Базы данных, например, на самом деле даже не имеют смысла линейного чтения, и, вероятно, никто даже не подумает о предоставлении файла поддержки базы данных путем замены процесса. Но PDF - это своего рода плакат для нелинейной обработки, и это иногда удивляет.

-1

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

(man -t ls | ps2pdf - ~/man_ls.pdf) > evince

Это создаст файл man_ls.pdf в вашем домашнем каталоге

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .