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

4

Estou tentando extrair todas as sinopses de comandos das páginas de manual em /usr/share/man/man1 usando:

#!/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 fornece alguma medida de sucesso - recebo saída completa para comandos de a para z . Mas também estou recebendo muitos erros em stderr usando for i in ./*.gz; do man "$i" e for i in *.gz; do man "./$i" conforme eu saio 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

Quais são os erros <standard input> sobre (algo escapou?) e por que man não está encontrando alguns arquivos? Como eu poderia tornar isso mais robusto / eficiente?

1. Parece que os erros em stderr são os mesmos, qualquer que seja a implementação / solução que eu uso para os mesmos dados. É impressionante.

    
por jus cogens prime 14.06.2014 / 08:08

2 respostas

4

Você não pode simplesmente executar man foo.gz Parece que você pode executar man foo.1.gz , mas usar -l parece 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 comando awk que dou funciona melhor que sua abordagem (teste em man ajc , por exemplo) e agora também funciona em sinopses com várias linhas. A maioria dos erros que você vê são irrelevantes, outros foram devido à maneira como você lidava com os nomes dos arquivos. Deixe-me saber se este funciona melhor.

    
por 14.06.2014 / 14:46
2

Com relação aos erros encontrados, todos são abordados aqui:

man man

MANWIDTH - If $MANWIDTH is set, its value is used as the line length for which manual pages should be formatted. If it is not set, manual pages will be formatted with a line length appropriate to the current terminal (using an ioctl(2) if available, the value of $COLUMNS, or falling back to 80 characters if neither is available). Cat pages will only be saved when the default formatting can be used, that is when the terminal line length is between 66 and 80 characters.

MAN_KEEP_FORMATTING - Normally, when output is not being directed to a terminal (such as to a file or a pipe), formatting characters are discarded to make it easier to read the result without special tools. However, if $MAN_KEEP_FORMATTING is set to any non-empty value, these formatting characters are retained. This may be useful for wrappers around man that can interpret formatting characters.

MAN_KEEP_STDERR - Normally, when output is being directed to a terminal (usually to a pager), any error output from the command used to produce formatted versions of manual pages is discarded to avoid interfering with the pager's display. Programs such as groff often produce relatively minor error messages about typographical problems such as poor alignment, which are unsightly and generally confusing when dis- played along with the manual page. However, some users want to see them anyway, so, if $MAN_KEEP_STDERR is set to any non-empty value, error output will be displayed as usual.

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

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///
    s/.\x08//g;s/\(\n\)  *//g;
    w /dev/stderr' -ne '};/./q'" -l "$f"
done 2>~/file

Especifica que sed seja o PAGER e, em seguida, produza apenas a linha que segue NAME e as seguintes SYNOPSIS até encontrar qualquer outra linha que comece com qualquer outra do que <space> . Ele imprime nada se a primeira linha que não começa com <space> que segue NAME não coincide com [Ss][Yy][Nn] . Em todos os casos, ele sai lendo o arquivo completamente na segunda linha que encontra após NAME , que não começa com <space> . Ele limpa o <spaces> e todos os \b ackslashes da saída.

Eu corri no loop for agora e ele passou por toda a minha biblioteca man em apenas um minuto.

man ajusta sua saída com base no fato de gravar em um terminal ou em um pipe / arquivo. Então, se você disser para fazer isso, ele desistirá completamente da PAGER. Isso foi inesperado. Mas eu o enganei e usei a função w rite do sed para escrever para > & 2 e redirecioná-la para que não fosse mais sensato.

Uma nota - embora - pode ser que o @dondon's seja o melhor caminho a percorrer. Embora você possa personalizar isso com mais facilidade porque você obtém um sed por arquivo, e a formatação é um pouco melhor porque não tenta ajustar a largura de um terminal, aparentemente man não grava essas \ backslashes em |pipe .

    
por 15.06.2014 / 07:51