Pourquoi les commandes intégrées au shell n'ont-elles pas de pages de manuel appropriées?

32

Toutes les commandes intégrées au shell partagent la même page de manuel:

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

NAME
     builtin, !

etc.

Ensuite, il y a un petit texte décrivant ce que sont les éléments intégrés au shell, puis une liste qui ressemble à ceci:

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

Mais si nous man grepobtenons nous obtenons des sections telles que

  • Bogues
  • Histoire
  • Voir également
  • Normes
  • La description

etc.

Les commandes intégrées au shell n'ont-elles pas leur propre histoire, description et arguments tels que -Aou -r? Pourquoi n'est-ce pas fourni dans les pages de manuel et comment pourrais-je apprendre à les utiliser correctement et efficacement?

Afficher un nom
la source

Réponses:

25

Parce que les éléments intégrés font partie de la coque. Tous les bugs ou leur histoire sont des bugs et l’histoire du shell lui-même. Ce ne sont pas des commandes indépendantes et n'existent pas en dehors du shell dans lequel elles sont construites.

L'équivalent, bashau moins, est la helpcommande. Par exemple:

$ 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.

Tous les programmes intégrés bash ont des helppages. Même helplui-même:

$ 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.

Inspiré du sedscript de @ mikeserv , voici une petite fonction permettant d’imprimer la section pertinente d’une page de manuel à l’aide de Perl. Ajoutez cette ligne au fichier d’initialisation de votre shell ( ~/.bashrcpour bash):

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

Ensuite, vous l’exécutez en lui donnant une page de manuel et le nom d’une section:

$ 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
la source
2
@DisplayName ils sont bash. Ils en font partie et oui, ils sont expliqués dans la SHELL BUILTIN COMMANDSsection de la bashpage de manuel. Leurs "pages de manuel" sont help builtin_name.
Terdon
3
Ce qui n'est pas clair, c'est pourquoi ils n'ont pas reçu de pages de manuel. Les pages de manuel ne sont que des fichiers sur le MANPATH. Ils ne doivent pas nécessairement correspondre à des fichiers binaires distincts. Rien n'empêche en principe bash de fournir des pages de manuel pour bash, plutôt que de disposer d'un système d'aide interne.
Francis Davey
4
@FrancisDavey: Mais la plupart des fonctions intégrées existent (avec des extensions différentes) dans différents shells. Les pages de manuel ne sont pas spécifiques à un shell; ils sont à l'échelle du système.
Rici
2
@FrancisDavey Comme l'a dit rici, les commandes ne sont pas à l'échelle du système. Il serait un peu trompeur d’avoir une page de manuel pour une commande qui ne soit pas présente dans chaque shell, mais pire encore, il serait très déroutant d’avoir une page de manuel pour une commande qui est présente dans plusieurs shells, mais qui se comporte différemment (par exemple, , accepte différents arguments, a une syntaxe différente, etc.).
Joshua Taylor
1
@mikeserv Cependant, je souhaiterais que les pages de manuel relatives aux shell construits dans le style de ce que, par exemple, offre git, man git commitaffiche une page de manuel pour git-commit. Quelque chose comme ce man bash ifserait merveilleux .
Joshua Taylor
5

Il est vrai que certaines commandes intégrées dans un shell peuvent ne figurer que dans un manuel complet - en particulier pour les commandes bashspécifiques à une utilisation spécifique que vous ne pourrez utiliser que sur un système GNU (en général, ne croyez pas manet préfèrent leurs propres infopages) - la grande majorité des utilitaires POSIX - intégrés au shell ou autrement - sont très bien représentés dans le Guide du programmeur POSIX.

Voici un extrait du bas de ma page man sh (qui fait probablement environ 20 pages ...)

entrez la description de l'image ici

Tous ces éléments sont là, et d' autres ne sont pas mentionnés tels que set, read, break... eh bien, je ne ai pas besoin de les nommer tous. Mais notez le (1P)en bas à droite - il désigne la série de manuels POSIX de catégorie 1 - ce sont les manpages dont je parle.

Il se peut que vous ayez juste besoin d'installer un paquet? Cela semble prometteur pour un système Debian. Bien que ce helpsoit utile, si vous pouvez le trouver, vous devriez absolument avoir cette POSIX Programmer's Guidesérie. Cela peut être extrêmement utile. Et ses pages constitutives sont très détaillées.

Cela dit, les commandes intégrées au shell sont presque toujours répertoriées dans une section spécifique du manuel du shell. zsh, par exemple, a une manpage entièrement séparée pour cela - (je pense qu’elle totalise environ 8 ou 9 zshpages individuelles - zshallce qui est énorme.)

Vous pouvez grep manbien sûr:

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

... ce qui est assez proche de ce que je faisais lorsque je cherchais une manpage shell . Mais helpc'est très bien bashdans la plupart des cas.

Je travaille actuellement sur un sedscript pour gérer ce genre de choses récemment. C'est ainsi que j'ai saisi la section de l'image ci-dessus. C'est encore plus long que je ne le souhaite, mais cela s'améliore - et peut être très pratique. Dans son itération actuelle, il va extraire de manière assez fiable une section de texte sensible au contexte correspondant à un en-tête de section ou de sous-section en fonction de [un] motif [s] donné sur la ligne de commande. Il colore sa sortie et l’imprime sur la sortie standard.

Cela fonctionne en évaluant les niveaux de retrait. Les lignes d'entrée non vierges sont généralement ignorées, mais lorsqu'elles rencontrent une ligne vierge, elles commencent à attirer l'attention. Il en rassemble les lignes jusqu'à ce qu'il ait vérifié que la séquence en cours est définitivement plus indentée que sa première ligne avant qu'une autre ligne vide ne se produise, sinon il supprime le fil et attend le prochain blanc. Si le test réussit, il tente de faire correspondre la ligne principale avec ses arguments de ligne de commande.

Cela signifie qu'un match de motif sera Match:

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

..et..

match
   text

..mais non..

heading
    match
    match

    notmatch

..ou..

         text

         match
         match
         text

         more text

Si une correspondance peut être obtenue, elle commence à imprimer. Elle supprime toutes les lignes vierges des espaces vides de la ligne correspondante. Ainsi, quel que soit le niveau de retrait, elle trouve cette ligne imprimée comme si elle se trouvait en haut. Il continuera à imprimer jusqu'à ce qu'il rencontre une autre ligne d'un niveau égal ou inférieur à celui de la ligne correspondante. Ainsi, des sections entières sont capturées avec uniquement une correspondance de titre, y compris une ou toutes les sous-sections qu'elles peuvent contenir.

En gros, si vous lui demandez de faire correspondre un motif, il le fera uniquement contre un en-tête de sujet et colorera et imprimera tout le texte trouvé dans la section précédée de sa correspondance. Rien n'est enregistré car cela fait cela, sauf l'indentation de votre première ligne. Elle peut donc être très rapide et gérer \ndes entrées séparées par ewline de toutes les tailles.

Il m'a fallu un certain temps pour comprendre comment rentrer dans les sous-titres suivants:

Section Heading
    Subsection Heading

Mais j'ai finalement résolu le problème.

J'ai dû retravailler le tout pour des raisons de simplicité, cependant. Alors qu'avant j'avais plusieurs petites boucles faisant la plupart du temps les mêmes choses de manières légèrement différentes pour s'adapter à leur contexte, en faisant varier les méthodes de récurrence, j'ai réussi à dédupliquer la majorité du code. Maintenant, il y a deux boucles: une en impression et une en retrait. Les deux dépendent du même test - la boucle d'impression commence lorsque le test est réussi et la boucle d'indentation prend le relais en cas d'échec ou de début d'une ligne vierge.

L'ensemble du processus est très rapide, car la plupart du temps, il /./dsupprime simplement les lignes non vierges et passe à la suivante - les résultats sont même générés zshallinstantanément à l'écran. Cela n'a pas changé.

Quoi qu'il en soit, c'est très utile jusqu'à présent, cependant. Par exemple, la readchose ci-dessus peut être faite comme:

mansed bash read

... et ça fait tout le bloc. Cela peut prendre n'importe quel modèle, peu importe, ou plusieurs arguments, bien que le premier soit toujours la manpage dans laquelle il doit chercher. Voici une image de certaines de ses sorties après que je l'ai fait:

mansed bash read printf

entrez la description de l'image ici

... les deux blocs sont retournés entiers. Je l'utilise souvent comme:

mansed ksh '[Cc]ommand.*'

... pour lequel c'est très utile. De plus, obtenir le SYNOPS[ES]rend vraiment pratique:

entrez la description de l'image ici

La voici si vous voulez faire un tourbillon - je ne vous blâmerai pas si vous ne le faites pas.

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
");}

En bref, le flux de travail est:

  • toute ligne qui n'est pas vide et qui ne contient pas de \ncaractère ewline est supprimée de la sortie.
    • \nLes caractères de ligne de texte ne se produisent jamais dans l'espace du modèle d'entrée. Ils ne peuvent être obtenus qu'à la suite d'une modification.
  • :printet :indentsont à la fois des boucles fermées mutuellement dépendantes et sont le seul moyen d'obtenir une \newline.
    • :printLe cycle de la boucle commence si les premiers caractères d'une ligne sont une série de blancs suivis d'un \ncaractère de ligne de ligne.
    • :indentLe cycle de commence sur les lignes vierges - ou sur les :printlignes de cycle qui échouent #test- mais :indentsupprime toutes les \nséquences vierges + lignes principales de sa sortie.
    • une fois :printqu’il commence, il continue à insérer les lignes d’entrée, à éliminer les espaces blancs jusqu’à la valeur trouvée sur la première ligne de son cycle, à traduire la surimpression et les échappements arrière en suréchantillons en sorties couleur et à imprimer les résultats jusqu’à #testéchec.
    • avant de :indentcommencer, il vérifie d'abord l' hancien espace pour la poursuite de l'indentation éventuelle (telle qu'une sous-section) , puis continue à extraire l'entrée tant que la ligne #testéchoue et que toute ligne suivant la première continue à correspondre [-. Lorsqu'une ligne après la première ne correspond pas à ce modèle, elle est supprimée et toutes les lignes suivantes le sont ensuite jusqu'à la ligne vierge suivante.
  • #matchet #testpontez les deux boucles fermées.
    • #testpasse lorsque la première série de blancs est plus courte que la série suivie de la dernière \nligne d'une ligne.
    • #matchajoute au \ndébut des étapes les lignes principales nécessaires au début d'un :printcycle à l'une des :indentséquences de sortie de qui correspondent à un argument de ligne de commande. Les séquences qui ne le sont pas sont rendues vides - et la ligne vide résultante est renvoyée à :indent.
Mikeserv
la source
2
Votre sed-fu est fort. Bien sûr, vous pouvez faire la même chose avec manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }et ensuite manperl sh SYNOPSISou manperl sh read:)
terdon
@terdon - non, vous ne pouvez pas. Cela ne mange pas d'entrée. Je pourrais faire la même chose comme ça sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'… probablement que cela fonctionne… mais cela nécessite d’avaler le fichier et de l’analyser en une fois. Cela fonctionne dans un flux - il peut gérer une entrée de n'importe quelle taille à condition que les lignes ne soient pas astronomiquement longues. Il imprime comme il fonctionne - et il analyse toutes manles \béchappements ackslash pour démarrer. Mais ce mann’est qu’une application unique - j’en ai appliqué une grande partie à d’autres problèmes ...
mikeserv
1
Je tire juste votre chaîne puisque je peux faire ce que vous décrivez avec un tout petit liner. Notez cependant que cela n’avale pas l’ensemble du fichier, cela fonctionne dans un flux. Il définit simplement les "lignes" en utilisant \n\nau lieu de, \nmais peut toujours gérer une entrée de taille et imprime comme il fonctionne Voir "mode paragraphe" ici: perldoc.perl.org/perlrun.html
terdon
@terdon Cela aurait peut-être été une meilleure façon de s'y rendre. Dans sedce qui peut être fait comme: '/./{H;$!d' -e '};x;now work the paragraph...'. Je le fais souvent aussi. Mais j’avais initialement écrit la première partie pour regarder un journal en direct pendant une durée illimitée, et même ce comportement était douteux - la mémoire tampon peut exploser dans certaines conditions. Ce n'était que la moitié de cette taille - le manrendait plus difficile. Cependant, j'ai regardé man -Haprès avoir obtenu la mansynop ci-dessus, et je pense qu'il serait peut-être plus facile de travailler avec le code HTML généré par la machine que groff peut imprimer sur les systèmes GNU. Je suis déjà au coude à coude
mikeserv
@terdon - Je me suis posé des questions moi-même et j'ai essayé une approche centrée sur les paragraphes, mais c'est plus facile en l'état. Cela obtient des sections. Comme mansed cmd DESCRIPTIONobtient la section DESCRIPTION - et tous ceux inclus. Une recherche correspondante est imprimée entière et comme si son niveau de retrait était le premier. Il ignore même les faux positifs en ignorant les paragraphes qui correspondent mais qui ne sont pas indentés davantage. Il correspond à ses arguments grâce aux échappements de retour de couleur et ne les gère pas jusqu'à ce qu'il soit vraiment prêt à imprimer une ligne. Il m'est très difficile de faire tout cela avec beaucoup plus de données qu'une seule ligne à la fois.
mikeserv
1

Chaque shell a son propre ensemble de commandes intégrées. Bien qu'il y ait des points communs, chacun a ses particularités qui doivent être documentées.

Sur des systèmes tels que Linux et FreeBSD (et OSX, qui hérite de FreeBSD) où chaque shell est fourni dans un package séparé, il n’existe pas de page de manuel pour les éléments intégrés; à la place, chaque élément intégré est documenté dans la page de manuel du shell. Lisez donc la page de manuel bash pour la documentation de la fonction killintégrée de bash , lisez la page de manuel dash pour la documentation de la fonction killintégrée de dash, etc. Il existe également une page de manuel pour l' killutilitaire autonome.

Voir Puis-je obtenir des pages de manuel individuelles pour les commandes intégrées bash? pour une manfonction qui affiche la documentation interne de bash à la place de la page de manuel si l'argument est le nom d'une commande intégrée.

Il existe des variantes unix qui fournissent des pages de manuel pour les commandes intégrées au shell - en fait, la plupart des variantes commerciales le sont. C'est faisable car le système est livré avec un seul shell ou un ensemble de shell connus. La page de manuel traite des différences entre les coquilles. Par exemple, la fg(1)page de manuel de Solaris 10 contient des sections pour sh, kshet csh. La fg(1)page de manuel sur AIX 7.1 fait référence à «Korn shell» et à «POSIX shell», mais les traite ensemble (ils prennent en charge les mêmes fonctionnalités pour fg). La fg(1)page de manuel de Tru64 5.0 décrit la structure intégrée de ksh et renvoie les utilisateurs de csh à la csh(1)page de manuel. SCOvient apparemment avec une seule coquille. Vous pouvez installer d'autres shells en tant que packages complémentaires sur ces systèmes d'exploitation. Si vous utilisez un shell personnalisé, vous devez vous rappeler que les pages de manuel relatives aux commandes intégrées ne seront pas pertinentes si vous utilisez un shell autre que celui par défaut.

Gilles, arrête de faire le mal
la source