Как правильно извлечь все описания команд из страниц руководства в /usr/share/man/man1?

Как правильно извлечь все описания команд из страниц руководства в /usr/share/man/man1?

Я пытаюсь извлечь всесводки командиз страниц руководства по /usr/share/man/man1использованию:

#!/usr/bin/env bash
## synopses - extract all synopses in /usr/share/man/man1

cd /usr/share/man/man1
for i in *.gz; do
    echo "$i:" | sed -E "s/.1.gz|.gz//g"
    man "./$i" | sed -n '/^SYNOPSIS/,/^[A-Z][A-Z][A-Z]/p' | sed -e '1d; $d' | tr -s [:space:]
done

...которая обеспечиваетнекоторыймера успеха - я получаю полный вывод для команд отакз. Но я также получаю много ошибок наstderrиспользуя оба for i in ./*.gz; do man "$i"иfor i in *.gz; do man "./$i"как я вывожу в файл( synopses > file) 1 :

<standard input>:27: expected `;' after scale-indicator (got `o')
<standard input>:29: expected `;' after scale-indicator (got `o')
<standard input>:283: name expected (got `\{'): treated as missing
<standard input>:674: warning: macro `as',' not defined (possibly missing space after `as')
<standard input>:174: name expected (got `\{'): treated as missing
<standard input>:161: warning [p 1, 5.5i]: can't break line
<standard input>:594: warning [p 5, 3.8i, div `an-div', 0.0i]: can't break line
<standard input>:569: warning [p 6, 0.0i]: can't break line
<standard input>:147: warning [p 1, 1.8i]: can't break line
<standard input>:205: warning [p 2, 0.2i]: can't break line
<standard input>:525: warning [p 5, 4.5i]: can't break line
<standard input>:157: warning [p 1, 4.8i]: can't break line
<standard input>:351: warning [p 3, 1.8i, div `an-div', 0.0i]: can't break line
<standard input>:147: a space character is not allowed in an escape name
man: can't open man1/zshmisc.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshexpn.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshparam.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshoptions.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshbuiltins.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshzle.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshcompwid.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshcompsys.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshcompctl.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshmodules.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshcalsys.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshtcpsys.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshzftpsys.1: No such file or directory
man: -:423: warning: failed .so request
man: can't open man1/zshcontrib.1: No such file or directory
man: -:423: warning: failed .so request
<standard input>:423: can't open `man1/zshmisc.1': No such file or directory
<standard input>:424: can't open `man1/zshexpn.1': No such file or directory
<standard input>:425: can't open `man1/zshparam.1': No such file or directory
<standard input>:426: can't open `man1/zshoptions.1': No such file or directory
<standard input>:427: can't open `man1/zshbuiltins.1': No such file or directory
<standard input>:428: can't open `man1/zshzle.1': No such file or directory
<standard input>:429: can't open `man1/zshcompwid.1': No such file or directory
<standard input>:430: can't open `man1/zshcompsys.1': No such file or directory
<standard input>:431: can't open `man1/zshcompctl.1': No such file or directory
<standard input>:432: can't open `man1/zshmodules.1': No such file or directory
<standard input>:433: can't open `man1/zshcalsys.1': No such file or directory
<standard input>:434: can't open `man1/zshtcpsys.1': No such file or directory
<standard input>:435: can't open `man1/zshzftpsys.1': No such file or directory
<standard input>:436: can't open `man1/zshcontrib.1': No such file or directory

Что это <standard input>за ошибки (что-то ускользнуло?) и почему manв итоге не находят некоторые файлы? Как сделать это более надежным/эффективным?


1. Кажется, ошибки наstderrодинаковы, какую бы реализацию/решение я ни использовал для одних и тех же данных. Это поразительно.

решение1

Ты не можешь просто бежатьman foo.gzПохоже на тебяможетзапустить man foo.1.gz, но использование -lкажется чище. От man man:

   -l, --local-file
          Activate `local' mode.  Format and display  local  manual  files
          instead  of  searching  through  the system's manual collection.
          Each manual page argument will be interpreted as an nroff source
          file in the correct format.  No cat file is produced.  If '-' is
          listed as one of the arguments, input will be taken from  stdin.
          When  this  option  is  not used, and man fails to find the page
          required, before displaying the error message,  it  attempts  to
          act as if this option was supplied, using the name as a filename
          and looking for an exact match.

Итак, ваш сценарий должен быть примерно таким:

#!/usr/bin/env bash
## synopses - extract all synopses in /usr/share/man/man1

## No need to cd into the directory, you can just use globs     
for i in /usr/share/man/man1/ajc*.gz; do
    ## This will print the name of the command.      
    basename "${i//.1.gz}"
    man -l "$i"  | 
       awk '/^SYNOPSIS/{a=1; getline}
            (/^[a-zA-z0-9_]/ && a==1){a=0} 
            (a==1 && /./){print}' | tr -s [:space:]

done

Команда awk, которую я даю, работает лучше, чем ваш подход (протестируйте ее, man ajcнапример), а теперь также работает с многострочными синопсисами. Большинство ошибок, которые вы видите, не имеют значения, другие были связаны с тем, как вы обрабатывали имена файлов. Дайте мне знать, если эта сработает лучше.

решение2

Что касается ошибок, с которыми вы сталкиваетесь, то все они рассмотрены здесь:

man man

MANWIDTH- Если $MANWIDTHустановлено, его значение используется как длина строки, для которой должны быть отформатированы страницы руководства. Если не установлено, страницы руководства будут отформатированы с длиной строки, соответствующей текущему терминалу (используя ioctl(2), если доступно, значение $COLUMNSили возвращаясь к 80 символам, если ни то, ни другое не доступно). Страницы cat будут сохранены только в том случае, если можно использовать форматирование по умолчанию, то есть когда длина строки терминала составляет от 66 до 80 символов.

MAN_KEEP_FORMATTING- Обычно, когда вывод не направляется на терминал (например, в файл или канал), символы форматирования отбрасываются, чтобы упростить чтение результата без специальных инструментов. Однако, если $MAN_KEEP_FORMATTINGустановлено любое непустое значение, эти символы форматирования сохраняются. Это может быть полезно для оболочек вокруг man, которые могут интерпретировать символы форматирования.

MAN_KEEP_STDERR- Обычно, когда вывод направляется на терминал (обычно на пейджер), любой вывод ошибок из команды, используемой для создания форматированных версий страниц руководства, отбрасывается, чтобы не мешать отображению на пейджере. Такие программы, как groffчасто выдают относительно незначительные сообщения об ошибках, связанных с типографскими проблемами, такими как плохое выравнивание, которые выглядят неприглядно и в целом сбивают с толку при отображении вместе со страницей руководства. Однако некоторые пользователи хотят видеть их в любом случае, поэтому, если $MAN_KEEP_STDERRустановлено любое непустое значение, вывод ошибок будет отображаться как обычно.

А теперь о том, как можно сделать другое:

Я думаю, это то, что вам нужно:

for f in /usr/share/man/man1/*gz ; do
    man -P "sed -ne '1,/^[Nn]/d;/^ /{H;b}
    /^[Ss]..[Yy]..[Nn]/{g;:n
    N;/\n\(\n\)[^ ].*/!bn;s//\1/
    s/.\x08//g;s/\(\n\)  */\1/g;
    w /dev/stderr' -ne '};/./q'" -l "$f"
done 2>~/file

Он указывает, что sedбудет PAGERи затем выводит только строку, следующую заИМЯи те, кто следуютСИНОПСИСпока не встретит любую другую строку, начинающуюся с чего-либо, кроме a <space>. Он печатаетничегоесли первая строка не начинается с <space>этого, следуетИМЯне соответствует begin [Ss][Yy][Nn]. В каждом случае он прекращает чтение файла на второй строке, которую он встречает послеИМЯ, который не начинается с <space>. Он очищает начальные <spaces>и все \bкосые черты из вывода.

Я только что запустил его в forцикле, и он прошел по всей моей manбиблиотеке всего за минуту.

manкорректирует свой вывод в зависимости от того, пишет ли он в терминал или в канал/файл. Так что если вы скажете ему сделать это, он вообще откажется от PAGER. Это было неожиданно. Но я обманул его и использовал wфункцию rite sed, чтобы записать в >&2 и перенаправить ее, так что он не стал умнее.

Примечание - хотя - возможно, это будет лучшим способом @terdon. Хотя вы можете настроить это проще, потому что вы получаете sedпо файлу, и форматирование немного лучше, потому что оно не пытается соответствовать ширине терминала, по-видимому, manне записывает эти \обратные косые черты в |pipe.

Связанный контент