¿Por qué las construcciones de shell no tienen páginas de manual adecuadas?

32

Todos los componentes de shell comparten la misma página de manual

BUILTIN(1)                BSD General Commands Manual               BUILTIN(1)

NAME
     builtin, !

etc.

Luego hay un pequeño texto que describe qué son los componentes integrados de shell, y luego una lista que se ve así:

  Command       External    csh(1)    sh(1)
       !             No          No        Yes
       %             No          Yes       No

Pero si lo hacemos man grep, obtenemos secciones como

  • Loco
  • Historia
  • Ver también
  • Normas
  • Descripción

etc.

¿Las construcciones de conchas no tienen su propia historia, descripción y argumentos como -Ao -r? ¿Por qué no se proporciona eso en las páginas del manual y cómo aprendería a usarlas de manera correcta y eficiente?

Nombre para mostrar
fuente

Respuestas:

25

Porque las incorporaciones son parte del caparazón. Cualquier error o historial que tengan son errores e historial del propio shell. No son comandos independientes y no existen fuera del shell en el que están integrados.

El equivalente, bashal menos, es el helpcomando. Por ejemplo:

$ help while
while: while COMMANDS; do COMMANDS; done
    Execute commands as long as a test succeeds.

    Expand and execute COMMANDS as long as the final command in the
    `while' COMMANDS has an exit status of zero.

    Exit Status:
    Returns the status of the last command executed.

Todos los integrantes de bash tienen helppáginas. Incluso a helpsí mismo:

$ help help
help: help [-dms] [pattern ...]
    Display information about builtin commands.

    Displays brief summaries of builtin commands.  If PATTERN is
    specified, gives detailed help on all commands matching PATTERN,
    otherwise the list of help topics is printed.

    Options:
      -d    output short description for each topic
      -m    display usage in pseudo-manpage format
      -s    output only a short usage synopsis for each topic matching
        PATTERN

    Arguments:
      PATTERN   Pattern specifiying a help topic

    Exit Status:
    Returns success unless PATTERN is not found or an invalid option is given.

Inspirado por el sedscript de @ mikeserv , aquí hay una pequeña función que imprimirá la sección relevante de una página man usando Perl. Agregue esta línea al archivo de inicialización de su shell ( ~/.bashrcpara bash):

manperl(){ man "$1" | perl -00ne "print if /^\s*$2\b/"; }

Luego, lo ejecuta dándole una página de manual y el nombre de una sección:

$ manperl bash while
       while list-1; do list-2; done
       until list-1; do list-2; done
              The while command continuously executes the list list-2 as long as the last command in the list list-1 returns an exit
              status of zero.  The until command is identical to the while command, except that the test is negated; list-2 is  exe‐
              cuted  as  long  as the last command in list-1 returns a non-zero exit status.  The exit status of the while and until
              commands is the exit status of the last command executed in list-2, or zero if none was executed.

$ manperl grep SYNOPSIS
SYNOPSIS
       grep [OPTIONS] PATTERN [FILE...]
       grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]

$ manperl rsync "-r"
       -r, --recursive
              This tells rsync to copy directories recursively.  See also --dirs (-d).
terdon
fuente
2
@DisplayName son bash. Son parte de esto y sí, se explican en la SHELL BUILTIN COMMANDSsección de la bashpágina del manual. Sus "páginas man" son help builtin_name.
terdon
3
Lo que no está claro es por qué no se les dieron páginas de manual. Las páginas man son solo archivos en MANPATH. No tienen que corresponder a binarios separados. En principio, no hay ninguna razón por la que bash no haya podido enviar páginas de manual para sus componentes, en lugar de tener un sistema de ayuda interno.
Francis Davey
44
@FrancisDavey: Pero la mayoría de las construcciones existen (con diferentes extensiones) en varios depósitos. Las páginas de manual no son específicas de shell; Son de todo el sistema.
rici
2
@FrancisDavey Como dijo rici, los comandos no son de todo el sistema. Sería un poco engañoso tener una página de manual para un comando que no está presente en cada shell, pero aún peor, sería muy confuso tener una página de manual para un comando que esté presente en múltiples shells, pero que se comporta de manera diferente (p. Ej. , acepta diferentes argumentos, tiene una sintaxis diferente, etc.).
Joshua Taylor
1
@mikeserv Sin embargo, me gustaría dar la bienvenida páginas del manual de órdenes internas a lo largo de las líneas de lo que, por ejemplo, ofertas GIT, donde man git commitaparece una página del manual de git-commit. Algo así man bash ifsería maravilloso .
Joshua Taylor
5

Si bien es cierto que algunos componentes integrados de shell pueden tener una escasa presentación en un manual completo, especialmente para aquellos bashcomponentes específicos que solo es probable que use en un sistema GNU (la gente de GNU, por regla general, no cree many prefieren sus propias infopáginas) - la gran mayoría de las utilidades POSIX - shell incorporadas o no - están muy bien representadas en la Guía del Programador POSIX.

Aquí hay un extracto de la parte inferior de mi man sh (que probablemente tenga más de 20 páginas ...)

ingrese la descripción de la imagen aquí

Todos los que están allí, y otros no mencionados, como set, read, break..., bueno, no necesito nombrarlos a todos. Pero tenga (1P)en cuenta que en la parte inferior derecha, denota la serie de manuales POSIX categoría 1, esas son las manpáginas de las que estoy hablando.

¿Puede ser que solo necesites instalar un paquete? Esto parece prometedor para un sistema Debian. Si bien helpes útil, si puede encontrarlo, definitivamente debería obtener esa POSIX Programmer's Guideserie. Puede ser extremadamente útil. Y sus páginas constituyentes son muy detalladas.

Aparte de eso, los componentes integrados de shell se enumeran casi siempre en una sección específica del manual de shell específico. zsh, por ejemplo, tiene una manpágina completa para eso (creo que totaliza 8 o 9 zshpáginas individuales , incluida la zshallque es enorme).

Por grep mansupuesto, puedes :

man bash 2>/dev/null | 
grep '^[[:blank:]]*read [^`]*[-[]' -A14

   read [-ers] [-a aname] [-d  delim]  [-i  text]  [-n
   nchars]  [-N  nchars]  [-p prompt] [-t timeout] [-u
   fd] [name ...]
          One line is read from the standard input, or
          from  the  file descriptor fd supplied as an
          argument to the -u  option,  and  the  first
          word is assigned to the first name, the sec‐
          ond word to the second name, and so on, with
          leftover words and their intervening separa‐
          tors assigned to the last  name.   If  there
          are  fewer  words read from the input stream
          than names, the remaining names are assigned
          empty  values.   The  characters  in IFS are
          used to split the line into words using  the
          same  rules  the  shell  uses  for expansion

... que está bastante cerca de lo que solía hacer cuando buscaba en una manpágina de shell . Pero helpes bastante bueno bashen la mayoría de los casos.

De hecho, he estado trabajando en un sedscript para manejar este tipo de cosas recientemente. Así es como agarré la sección en la imagen de arriba. Todavía es más largo de lo que me gusta, pero está mejorando y puede ser bastante útil. En su iteración actual, extraerá de manera bastante confiable una sección de texto sensible al contexto que coincida con un encabezado de sección o subsección basado en [a] patrón [s] dado en la línea de comando. Colorea su salida e imprime en stdout.

Funciona evaluando los niveles de sangría. Las líneas de entrada no en blanco generalmente se ignoran, pero cuando encuentra una línea en blanco comienza a prestar atención. Reúne líneas desde allí hasta que verifica que la secuencia actual definitivamente sangra más de lo que lo hizo su primera línea antes de que ocurra otra línea en blanco o de lo contrario deja caer el hilo y espera el siguiente espacio en blanco. Si la prueba es exitosa, intenta hacer coincidir la línea principal con sus argumentos de línea de comando.

Esto significa que un partido de patrón que coincida con:

heading
    match ...
    ...
    ...
        text...

..y..

match
   text

..pero no..

heading
    match
    match

    notmatch

..o..

         text

         match
         match
         text

         more text

Si se puede encontrar una coincidencia, comienza a imprimirse. Eliminará los espacios en blanco iniciales de la línea coincidente de todas las líneas que imprime, por lo que, sin importar el nivel de sangría, encontró que la línea en ella la imprime como si estuviera en la parte superior. Continuará imprimiendo hasta que encuentre otra línea en un nivel de sangría igual o menor que su línea coincidente, por lo que secciones enteras se capturan con solo una coincidencia de encabezado, incluidas cualquiera / todas las subsecciones, párrafos que pueden contener.

Básicamente, si le pide que coincida con un patrón, solo lo hará contra un encabezado de tema de algún tipo y coloreará e imprimirá todo el texto que encuentre dentro de la sección encabezada por su coincidencia. Nada se guarda mientras lo hace, excepto la sangría de su primera línea, por lo que puede ser muy rápido y manejar \nentradas separadas por línea de línea de prácticamente cualquier tamaño.

Me tomó un tiempo descubrir cómo recurrir a subtítulos como el siguiente:

Section Heading
    Subsection Heading

Pero lo resolví eventualmente.

Sin embargo, tuve que volver a trabajar todo por simplicidad. Mientras que antes tenía varios bucles pequeños que realizaban principalmente las mismas cosas de maneras ligeramente diferentes para adaptarse a su contexto, al variar sus medios de recursión, logré eliminar la duplicación de la mayoría del código. Ahora hay dos bucles: uno imprime y otro comprueba la sangría. Ambos dependen de la misma prueba: el ciclo de impresión comienza cuando pasa la prueba y el ciclo de sangría se hace cargo cuando falla o comienza en una línea en blanco.

Todo el proceso es muy rápido porque la mayoría de las veces solo /./delige cualquier línea que no esté en blanco y pasa a la siguiente, incluso los resultados de zshallllenar la pantalla al instante. Esto no ha cambiado.

De todos modos, hasta ahora es muy útil. Por ejemplo, readlo anterior se puede hacer como:

mansed bash read

... y obtiene todo el bloque. Puede tomar cualquier patrón o lo que sea, o múltiples argumentos, aunque el primero es siempre la manpágina en la que debe buscar. Aquí hay una foto de algunos de sus resultados después de que lo hice:

mansed bash read printf

ingrese la descripción de la imagen aquí

... ambos bloques se devuelven enteros. A menudo lo uso como:

mansed ksh '[Cc]ommand.*'

... para lo cual es bastante útil. Además, conseguirlo lo SYNOPS[ES]hace realmente útil:

ingrese la descripción de la imagen aquí

Aquí está si quieres darle un giro; no te culparé si no lo haces.

mansed() {
MAN_KEEP_FORMATTING=1 man "$1" 2>/dev/null | ( shift
b='[:blank:]' s='[:space:]' bs=$(printf \\b) esc=$(printf '\033\[') n='\
' match=$(printf "\([${b}]*%s[${b}].*\)*" "$@")
sed -n "1p
    /\n/!{  /./{    \$p;d
        };x;    /.*\n/!g;s///;x
    :indent
        /.*\n\n/{s///;x
        };n;\$p;
        /^\([^${s}].*\)*$/{s/./ &/;h;   b indent
        };x;    s/.*\n[^-[]*\n.*//; /./!x;t
        s/[${s}]*$//;   s/\n[${b}]\{2,\}/${n} /;G;h
    };
    #test
    /^\([${b}]*\)\([^${b}].*\n\)\1\([${b}]\)/!b indent
        s//\1\2.\3/
    :print
    /^[${s}]*\n\./{ s///;s/\n\./${n}/
        /${bs}/{s/\n/ & /g;
            s/\(\(.\)${bs}\2\)\{1,\}/${esc}38;5;35m&${esc}0m/g
            s/\(_${bs}[^_]\)\{1,\}/${esc}38;5;75m&${esc}0m/g
            s/.${bs}//g;s/ \n /${n}/g
            s/\(\(${esc}\)0m\2[^m]*m[_ ]\{,2\}\)\{2\}/_/g
        };p;g;N;/\n$/!D
        s//./;  t print
    };
    #match
        s/\n.*/ /;  s/.${bs}//g
        s/^\(${match}\).*/${n}\1/
        /../{   s/^\([${s}]*\)\(.*\)/\1${n}/
        x;  s//${n}\1${n}. \2/; P
    };D
");}

Brevemente, el flujo de trabajo es:

  • cualquier línea que no esté en blanco y que no contenga un \ncarácter ewline se eliminará de la salida.
    • \nLos caracteres ewline nunca aparecen en el espacio del patrón de entrada. Solo se pueden obtener como resultado de una edición.
  • :printy :indentson bucles cerrados mutuamente dependientes y son la única forma de obtener una línea \nelectrónica.
    • :printEl ciclo de bucle comienza si los caracteres principales de una línea son una serie de espacios en blanco seguidos de un \ncarácter de línea de hilo.
    • :indentEl ciclo comienza en las líneas en blanco, o en las :printlíneas de ciclo que fallan #test, pero :indentelimina todas las \nsecuencias iniciales en blanco + ewline de su salida.
    • una vez que :printcomience, continuará tirando de las líneas de entrada, eliminando los espacios en blanco iniciales hasta la cantidad encontrada en la primera línea de su ciclo, traduce los escapes de retroceso de sobretasa y subtrazo en escapes de terminales de color e imprime los resultados hasta que #testfalle.
    • antes de que :indentcomience, primero verifica el hespacio anterior para ver si hay una posible continuación de sangría (como una Subsección) , y luego continúa introduciendo la entrada siempre que #testfalle y cualquier línea que sigue a la primera continúa coincidiendo [-. Cuando una línea después de la primera no coincide con ese patrón, se elimina, y posteriormente también lo son todas las líneas siguientes hasta la siguiente línea en blanco.
  • #matchy #testunir los dos bucles cerrados.
    • #testpasa cuando la serie principal de espacios en blanco es más corta que la serie seguida por la última línea \new en una secuencia de línea.
    • #matchantepone los ewlines \niniciales necesarios para comenzar un :printciclo a cualquiera de :indentlas secuencias de salida que conducen con una coincidencia a cualquier arg de línea de comando. Esas secuencias que no se vuelven vacías, y la línea en blanco resultante se devuelve a :indent.
mikeserv
fuente
2
Tu sed-fu es fuerte. Por supuesto, puede hacer lo mismo con manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }y luego manperl sh SYNOPSISo manperl sh read:)
terdon
@terdon - no, no puedes. Esto no come información. Podría hacer lo mismo que eso sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'... probablemente eso funcione ... pero eso requiere tragar el archivo y analizarlo todo de una vez. Esto funciona en una secuencia: puede manejar entradas de cualquier tamaño siempre que las líneas no sean astronómicamente largas. Se imprime a medida que funciona, y analiza todos manlos \bescapes de ackslash para arrancar. Pero manes solo una aplicación única: he aplicado gran parte de ella a otros problemas también ...
mikeserv
1
Solo estoy tirando de su cadena ya que puedo hacer lo que usted describe con un pequeño forro. Sin embargo, tenga en cuenta que no se traga todo el archivo, funciona en una secuencia. Simplemente define "líneas" usando en \n\nlugar de, \npero aún puede manejar cualquier entrada de tamaño e imprime a medida que funciona. Ver "modo párrafo" aquí: perldoc.perl.org/perlrun.html
terdon
@terdon Tal vez esa hubiera sido una mejor manera de ir aquí. En sedella se puede hacer como: '/./{H;$!d' -e '};x;now work the paragraph...'. A menudo hago eso también. Pero originalmente escribí la primera parte para ver un registro en vivo durante un tiempo ilimitado, e incluso ese comportamiento era dudoso: el búfer puede explotar bajo ciertas condiciones. Eso era solo la mitad de este tamaño, lo manhizo más difícil. Sin embargo, miré man -Hdespués de obtener la mansinopsis anterior, y estoy pensando que podría ser más fácil trabajar con el HTML genérico que Groff puede imprimir en sistemas GNU. Ya estoy hasta el codo
mikeerv
@terdon: me hice una segunda suposición y probé un enfoque centrado en los párrafos, pero es más fácil como es. Esto obtiene secciones. Like mansed cmd DESCRIPTIONobtiene la sección DESCRIPTION, y todas las incluidas. Una búsqueda coincidente se imprime completa y como si su nivel de sangría fuera el primero. Incluso omite los falsos positivos al ignorar los párrafos que coinciden pero que luego no sangran más. Coincide con sus argumentos a través de los escapes de retroceso de color y no los maneja hasta que esté definitivamente listo para imprimir una línea. Todo eso es muy difícil para mí con mucha más información que una sola línea a la vez.
mikeserv
1

Cada caparazón tiene su propio conjunto de componentes incorporados. Si bien hay puntos en común, cada uno tiene sus propias peculiaridades que deben documentarse.

En sistemas como Linux y FreeBSD (y OSX, que hereda de FreeBSD) donde cada shell se proporciona como un paquete separado, no hay una página de manual para los componentes integrados; en cambio, cada uno de ellos está documentado en la página de manual del shell. Por lo tanto, lea la página de manual de bash para obtener la documentación de bash killincorporado, lea la página de manual de dash para la documentación de la función de dash kill, etc. También hay una página de manual para la killutilidad independiente.

Consulte ¿Puedo obtener páginas de manual individuales para los comandos incorporados de bash? para una manfunción que muestra la documentación interna de bash en lugar de la página de manual si el argumento es el nombre de un builtin incorporado.

Existen variantes de Unix que proporcionan páginas de manual para los componentes integrados de shell; de hecho, la mayoría de las variantes comerciales lo hacen. Eso es factible porque el sistema viene con un solo shell o un conjunto de shells conocidos. La página de manual discute las diferencias entre los shells. Por ejemplo, la fg(1)página de manual en Solaris 10 tiene secciones para sh, kshy csh. La fg(1)página del manual en AIX 7.1 hace referencia a "Korn shell" y "POSIX shell", pero los discute juntos (son compatibles con exactamente las mismas características para fg). La fg(1)página del manual en Tru64 5.0 discute el ksh incorporado y remite a los usuarios de csh a la csh(1)página del manual. SCOaparentemente viene con un solo caparazón. Puede instalar otros shells como paquetes complementarios en estos sistemas operativos; si usa un shell personalizado, debe recordar que las páginas de manual para los builtins no serán relevantes cuando use un shell no predeterminado.

Gilles 'SO- deja de ser malvado'
fuente