Использование процесса подстановки (или аналогичного) для отображения man-страниц в формате pdf без временных файлов

1

У меня есть функция Bash для отображения справочных страниц в виде 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, не создавая при этом никаких файлов.

Джонатан Хартли
источник
Я начинаю думать, что моя проблема связана с тем, как сам evince открывает и читает из файлов. Другие инструменты (такие как diff, как описано выше), по-видимому, без проблем открывают имя файла, полученное в результате подстановки моего процесса.
Джонатан Хартли
Я замечаю, что evince <( cat man-ls.pdf )открывается без ошибок, отображаются 4 страницы (правильное число), но все страницы пусты. Как будто он частично прочитал файл успешно, но затем потерпел неудачу в какой-то момент.
Джонатан Хартли
Я думаю, что, возможно, я должен был изложить
Джонатан Хартли

Ответы:

2

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

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сигнализировать, если хотите; Я решил запустить всю последовательность в background ( ( … ) &), что может быть достаточно.
  3. Мой evinceфайл не открывается, /dev/shmесли только его имя не заканчивается .pdf(это поведение не учитывает регистр). Вот почему есть .pdfв шаблоне имени файла. Там нет такой проблемы в /tmp. Зачем? Я не знаю.
  4. Шаблон имени файла создается $*в нем, чтобы сделать его несколько осмысленным (он отображается в заголовке evinceокна).
Камиль Мачоровски
источник
Это имеет большой смысл. Спасибо за ссылку. Я кое-что узнал сегодня.
Джонатан Хартли
1

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

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

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

RICi
источник
-1

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

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

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

Дженаро Моралес
источник
Спасибо за идеи, но я пока не понимаю. Вы уверены, что имели в виду «>» в ​​конце этого удара? Он пишет пустой файл под названием «evince»
Джонатан Хартли
Помните, моя цель состоит в том, чтобы запустить программу под названием «evince» (программа просмотра PDF-файлов gnome) в PDF, не записывая при этом никаких файлов.
Джонатан Хартли
Мои извенения. Я собираюсь пометить этот ответ, потому что команда не работает, и объяснение, похоже, не отвечает на мой вопрос вообще. Извиняюсь, если я неправильно истолковываю.
Джонатан Хартли
Почему вы пытаетесь написать PDF-файл без использования какого-либо файла? Вы должны где-то хранить информацию, если вы не пытаетесь получить временные файлы или какие-либо файлы, каков ваш подход?
Дженаро Моралес
Привет, Дженаро. В подходе Bash для этого используется функция, называемая подстановкой процесса с использованием cmd1 <( cmd2 )синтаксиса. Стандартный вывод cmd2(в моем примере ps2pdf) переходит в канал, и этому каналу присваивается имя в файловой системе, и это имя передается в cmd1 (в моем примере evince). cmd1 может открыть имя файла, которое ему было дано, прочитать его и получить стандартный вывод cmd2. Ни одна из команд не имеет ни малейшего представления о том, что используется подстановка процесса. Однако Bash ни в коем случае не записывает байты на диск. Это все в памяти, очень похоже на перенаправление и каналы. Дело в производительности и личном обучении.
Джонатан Хартли