Como você extrai corretamente todas as sinopses de comandos das páginas de manual em/usr/share/man/man1?

Como você extrai corretamente todas as sinopses de comandos das páginas de manual em/usr/share/man/man1?

Estou tentando extrair tudosinopses de comandodas páginas de manual em /usr/share/man/man1uso:

#!/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

...que fornecealgunsmedida de sucesso - obtenho resultados completos para comandos deaparaz. Mas também estou recebendo muitos errosstderrusando ambos for i in ./*.gz; do man "$i"efor i in *.gz; do man "./$i"conforme eu envio para o arquivo( 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

Sobre o que são esses <standard input>erros (algo escapou?) E por que manacaba não encontrando alguns arquivos? Como eu poderia tornar isso mais robusto/eficiente?


1. Parece que os erros emstderrsão os mesmos, independentemente da implementação/solução que uso para os mesmos dados. É impressionante.

Responder1

Você não pode simplesmente correrman foo.gzParece que vocêpodeexecute man foo.1.gz, mas usando o -lparece mais limpo. De 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.

Então, seu script deve ser algo como:

#!/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

O awkcomando que dou funciona melhor do que a sua abordagem (teste-o, man ajcpor exemplo) e agora também funciona em sinopses multilinhas. A maioria dos erros que você vê são irrelevantes, outros se devem à maneira como você lida com os nomes dos arquivos. Deixe-me saber se este funciona melhor.

Responder2

Em relação aos erros que você encontra, todos eles são abordados aqui:

man man

MANWIDTH- Se $MANWIDTHestiver definido, seu valor será usado como o comprimento da linha para a qual as páginas de manual devem ser formatadas. Se não estiver definido, as páginas de manual serão formatadas com um comprimento de linha apropriado ao terminal atual (usando um ioctl(2) se disponível, o valor de $COLUMNS, ou voltando para 80 caracteres se nenhum estiver disponível). As páginas Cat só serão salvas quando a formatação padrão puder ser usada, ou seja, quando o comprimento da linha do terminal estiver entre 66 e 80 caracteres.

MAN_KEEP_FORMATTING- Normalmente, quando a saída não está sendo direcionada para um terminal (como um arquivo ou pipe), os caracteres de formatação são descartados para facilitar a leitura do resultado sem ferramentas especiais. No entanto, se $MAN_KEEP_FORMATTINGfor definido com qualquer valor não vazio, esses caracteres de formatação serão mantidos. Isso pode ser útil para wrappers man que podem interpretar caracteres de formatação.

MAN_KEEP_STDERR- Normalmente, quando a saída está sendo direcionada para um terminal (geralmente para um pager), qualquer saída de erro do comando usado para produzir versões formatadas de páginas de manual é descartada para evitar interferir na exibição do pager. Programas como esse groffgeralmente produzem mensagens de erro relativamente pequenas sobre problemas tipográficos, como mau alinhamento, que são desagradáveis ​​e geralmente confusas quando exibidas junto com a página de manual. No entanto, alguns usuários desejam vê-los de qualquer maneira, portanto, se $MAN_KEEP_STDERRfor definido como qualquer valor não vazio, a saída de erro será exibida normalmente.

E agora sobre como você pode fazer a outra coisa:

Eu acho que isso faz o que você quer:

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

Ele especifica que sedseja o PAGERe então gera apenas a linha seguinteNOMEe aqueles que seguemSINOPSEaté encontrar qualquer outra linha começando com algo diferente de a <space>. Ele imprimenadase a primeira linha não começar com <space>a seguinteNOMEnão corresponde a start [Ss][Yy][Nn]. Em todos os casos, ele para de ler o arquivo na segunda linha que encontra apósNOMEisso não começa com <space>. Ele limpa <spaces>todas \bas barras iniciais e de entrada da saída.

Eu executei o forloop agora há pouco e ele percorreu toda a minha manbiblioteca em apenas um minuto.

manajusta sua saída com base no fato de ela gravar em um terminal ou em um pipe/arquivo. Então, se você pedir para fazer isso, ele renunciará completamente ao PAGER. Isso foi inesperado. Mas eu enganei e usei wa função rite do sed para escrever para >&2 e redirecionei isso para que não fosse mais sábio.

Uma observação - porém - pode ser que @terdon seja o melhor caminho a seguir. Embora você possa adaptar isso mais facilmente porque obtém um sedarquivo por arquivo, e a formatação é um pouco melhor porque não tenta ajustar a largura do terminal, aparentemente mannão grava essas barras invertidas em um arquivo |pipe.

informação relacionada