Warum haben Shell-Buildins keine richtigen Manpages?

32

Alle Shell-Buildins haben dieselbe Handbuchseite:

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

NAME
     builtin, !

etc.

Dann gibt es einen kleinen Text, der beschreibt, was Shell-Builtins sind, und dann eine Liste, die ungefähr so ​​aussieht:

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

Aber wenn wir das tun man grep, bekommen wir Abschnitte wie

  • Bugs
  • Geschichte
  • Siehe auch
  • Standards
  • Beschreibung

etc.

Haben Shell-Buildins nicht ihre eigene Geschichte, Beschreibung und Argumentation wie -Aoder -r? Warum ist das in den Handbuchseiten nicht angegeben und wie würde ich lernen, sie richtig und effizient zu verwenden?

Anzeigename
quelle

Antworten:

25

Weil Builtins Teil der Shell sind. Alle Fehler oder die Historie, die sie haben, sind Fehler und die Historie der Shell. Sie sind keine unabhängigen Befehle und existieren nicht außerhalb der Shell, in die sie eingebaut sind.

bashZumindest das Äquivalent ist der helpBefehl. Beispielsweise:

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

Alle Bash-Buildins haben helpSeiten. Sogar helpselbst:

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

Inspiriert von @ mikeservs sedSkript ist hier eine kleine Funktion, die den relevanten Abschnitt einer Manpage mit Perl druckt. Fügen Sie diese Zeile zur Initialisierungsdatei Ihrer Shell hinzu ( ~/.bashrcfür bash):

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

Dann führen Sie es aus, indem Sie ihm eine Manpage und den Namen eines Abschnitts geben:

$ 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
quelle
2
@DisplayName sie sind Bash. Sie sind Teil davon und ja, sie werden im SHELL BUILTIN COMMANDSAbschnitt der bashManpage erklärt. Ihre "Manpages" sind help builtin_name.
Terdon
3
Was nicht klar ist, ist, warum ihnen keine Manpages gegeben wurden. Manpages sind nur Dateien auf dem MANPATH. Sie müssen keinen separaten Binärdateien entsprechen. Es gibt im Prinzip keinen Grund, warum bash keine Manpages für seine Buildins geliefert haben könnte - anstatt ein internes Hilfesystem zu haben.
Francis Davey
4
@FrancisDavey: Die meisten Buildins existieren (mit unterschiedlichen Erweiterungen) in verschiedenen Shells. Hilfeseiten sind nicht shellspezifisch. Sie sind systemweit.
rici
2
@FrancisDavey Wie rici sagte, sind die Befehle nicht systemweit. Es wäre ein bisschen eine Manpage für einen Befehl haben , irreführend , die in jeder Schale nicht vorhanden ist, aber noch schlimmer ist , wäre es sehr verwirrend einen Mann Seite für einen Befehl haben, ist in mehreren Schalen vorhanden, die aber anders verhält (zB , akzeptiert unterschiedliche Argumente, hat unterschiedliche Syntax usw.).
Joshua Taylor
1
@mikeserv Ich würde jedoch Manpages für Shell-Buildins begrüßen, ähnlich wie z. B. Git-Angebote, bei denen man git commiteine Manpage für aufgerufen wird git-commit. Sowas man bash ifwäre wunderbar .
Joshua Taylor
5

Es ist zwar richtig, dass einige Shell - Buildins in einem vollständigen Handbuch nur einen geringen Umfang aufweisen können - insbesondere für diejenigen - bashspezifischen Buildins, die Sie wahrscheinlich nur auf einem GNU - System verwenden (die GNU - Leute glauben in der Regel nicht an manund) bevorzugen ihre eigenen infoSeiten) - die überwiegende Mehrheit der POSIX-Dienstprogramme - Shell-Builtins oder andere - sind im POSIX-Programmierhandbuch sehr gut vertreten.

Hier ist ein Auszug aus dem unteren Teil von meinem man sh (der wahrscheinlich 20 Seiten lang ist oder so ...)

Bildbeschreibung hier eingeben

Alle diese sind da, und andere wie nicht erwähnt set , read, break... na ja, brauche ich nicht , sie alle zu nennen. Beachten Sie jedoch die (1P)unten rechts angezeigte POSIX-Handbuchserie der Kategorie 1 - das sind die manSeiten, über die ich spreche.

Es kann sein, dass Sie nur ein Paket installieren müssen? Dies sieht für ein Debian-System vielversprechend aus. Während helpes nützlich ist, wenn Sie es finden können, sollten Sie auf jeden Fall diese POSIX Programmer's GuideSerie bekommen. Das kann sehr hilfreich sein. Die einzelnen Seiten sind sehr detailliert.

Abgesehen davon werden Shell-Builtins fast immer in einem bestimmten Abschnitt des jeweiligen Shell-Handbuchs aufgeführt. zshZum Beispiel hat eine ganze separate manSeite dafür - (ich denke, es summiert sich auf 8 oder 9 oder so einzelne zshSeiten - einschließlich der, zshalldie riesig ist.)

Sie können nur grep man natürlich nur:

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

... was ziemlich genau dem entspricht, was ich früher beim Durchsuchen einer Shell gemacht habe man Seite gemacht habe. Aber helpist ziemlich gut in bashin den meisten Fällen.

Ich habe in sedletzter Zeit tatsächlich an einem Skript gearbeitet, um mit solchen Dingen umzugehen. So habe ich den Abschnitt im Bild oben aufgenommen. Es ist immer noch länger als ich es mag, aber es verbessert sich - und es kann ziemlich praktisch sein. In seiner aktuellen Iteration wird es einen kontextsensitiven Textabschnitt ziemlich zuverlässig extrahieren, der mit einem Abschnitt oder einer Unterabschnittsüberschrift übereinstimmt, basierend auf [a] pattern [s], das es in der Befehlszeile gibt. Es färbt seine Ausgabe und druckt nach Standard.

Es funktioniert durch Auswerten von Einzugsebenen. Nicht-leere Eingabezeilen werden im Allgemeinen ignoriert, aber wenn sie auf eine leere Zeile treffen, beginnt sie zu achten. Von dort sammelt es Zeilen, bis es überprüft hat, dass die aktuelle Sequenz definitiv weiter eingerückt ist als die erste Zeile, bevor eine weitere Leerzeile auftritt, oder es löscht den Thread und wartet auf das nächste Leerzeichen. Wenn der Test erfolgreich ist, wird versucht, die Führungslinie mit ihren Befehlszeilenargumenten abzugleichen.

Dies bedeutet, dass ein Übereinstimmungsmuster übereinstimmt mit:

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

..und..

match
   text

..aber nicht..

heading
    match
    match

    notmatch

..oder..

         text

         match
         match
         text

         more text

Wenn eine Übereinstimmung gefunden werden kann, wird der Druckvorgang gestartet. Dabei werden die führenden Leerzeichen der übereinstimmenden Zeile aus allen gedruckten Zeilen entfernt. Unabhängig von der Einrückungsstufe wird diese Zeile so gedruckt, als befänden sie sich oben. Es wird so lange gedruckt, bis es auf eine andere Zeile stößt, deren Einzug gleich oder geringer ist als der der übereinstimmenden Zeile. Daher werden ganze Abschnitte mit nur einer Überschriftenübereinstimmung erfasst, einschließlich aller Unterabschnitte und Absätze, die sie möglicherweise enthalten.

Wenn Sie also darum bitten, ein Muster abzugleichen, wird dies nur für eine Betreff-Überschrift durchgeführt und der gesamte Text innerhalb des Abschnitts mit der Überschrift für die Übereinstimmung wird gefärbt und gedruckt. Dabei wird nichts außer dem Einzug der ersten Zeile gespeichert. Dies ist sehr schnell und ermöglicht die Verarbeitung von \nE-Line-getrennten Eingaben in praktisch jeder Größe.

Es dauerte eine Weile, bis ich herausgefunden hatte, wie ich mich in Unterüberschriften wie die folgenden einteilen konnte:

Section Heading
    Subsection Heading

Aber ich habe es irgendwann geklärt.

Der Einfachheit halber musste ich das Ganze jedoch überarbeiten. Während ich zuvor mehrere kleine Schleifen hatte, die zumeist die gleichen Dinge auf etwas unterschiedliche Weise taten, um ihrem Kontext zu entsprechen, gelang es mir, durch Variation der Rekursionsmethoden den größten Teil des Codes zu de-duplizieren. Jetzt gibt es zwei Schleifen - eine druckt und eine prüft den Einzug. Beide hängen von demselben Test ab - die Druckschleife startet, wenn der Test bestanden wird, und die Einrückschleife übernimmt, wenn der Test fehlschlägt oder in einer leeren Zeile beginnt.

Der gesamte Vorgang ist sehr schnell, da in den meisten /./dFällen nur nicht leere Zeilen entfernt und zur nächsten Zeile gewechselt werden. Dies ergibt sich sogar aus zshalldem sofortigen Auffüllen des Bildschirms. Das hat sich nicht geändert.

Sowieso ist es bis jetzt sehr nützlich. Zum Beispiel kann das readoben Gesagte so gemacht werden:

mansed bash read

... und es wird der ganze Block. Es kann ein beliebiges Muster oder was auch immer oder mehrere Argumente annehmen, obwohl das erste immer die manSeite ist, auf der gesucht werden soll. Hier ist ein Bild von einigen seiner Ausgaben, nachdem ich es getan habe:

mansed bash read printf

Bildbeschreibung hier eingeben

... beide Blöcke werden als Ganzes zurückgegeben. Ich benutze es oft wie:

mansed ksh '[Cc]ommand.*'

... wofür es ganz nützlich ist. Außerdem SYNOPS[ES]macht das Erhalten es wirklich praktisch:

Bildbeschreibung hier eingeben

Hier ist es, wenn Sie es versuchen wollen - ich werde Ihnen keine Vorwürfe machen, wenn Sie es nicht tun.

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

Kurz gesagt, der Workflow ist:

  • Jede Zeile, die nicht leer ist und kein \newline-Zeichen enthält, wird aus der Ausgabe gelöscht.
    • \newline-Zeichen kommen im Eingabemusterraum nie vor. Sie können nur als Ergebnis einer Bearbeitung erhalten werden.
  • :printund :indentsind beide voneinander abhängige geschlossene Schleifen und der einzige Weg, eine \newline zu erhalten .
    • :printDer Schleifenzyklus von beginnt, wenn die führenden Zeichen in einer Zeile eine Reihe von Leerzeichen gefolgt von einem \newline-Zeichen sind.
    • :indentDer Zyklus beginnt in Leerzeilen - oder in :printZykluszeilen, die fehlschlagen #test-, :indententfernt jedoch alle führenden Leerzeichen + \newline-Sequenzen aus der Ausgabe.
    • Sobald der :printVorgang beginnt, werden die Eingabezeilen weiter eingezogen, führende Leerzeichen werden bis zu dem in der ersten Zeile des Zyklus angegebenen Wert entfernt, Über- und Unterstriche werden in Farbausblendungen umgewandelt und die Ergebnisse werden gedruckt, bis der #testVorgang fehlschlägt.
    • before :indentstarts prüft zuerst den halten Speicherplatz auf mögliche Einrückungsfortsetzungen (z. B. einen Unterabschnitt) und zieht dann die Eingabe weiter ein, solange dies #testfehlschlägt und jede Zeile, die auf die erste folgt, weiterhin übereinstimmt [-. Wenn eine Zeile nach der ersten nicht mit diesem Muster übereinstimmt, wird sie gelöscht. Anschließend werden alle folgenden Zeilen bis zur nächsten leeren Zeile gelöscht.
  • #matchund #testüberbrücken Sie die beiden geschlossenen Schleifen.
    • #testwird ausgeführt, wenn die führende Reihe von Leerzeichen kürzer ist als die Reihe, gefolgt von der letzten \newline in einer Zeilenfolge.
    • #matchStellt die führenden \nE- Zeilen, die zum Starten eines :printZyklus benötigt werden, vor die :indentAusgabesequenzen, die zu einer Übereinstimmung mit einem Befehlszeilenargument führen. Die Sequenzen, die nicht leer gerendert werden - und die resultierende Leerzeile wird an zurückgegeben :indent.
mikeserv
quelle
2
Dein Sed-Fu ist stark. Natürlich können Sie dasselbe mit manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }und dann manperl sh SYNOPSISoder machen manperl sh read:)
terdon
@terdon - nein, das kannst du nicht. Das frisst keinen Input. Ich könnte das Gleiche tun wie sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'... wahrscheinlich funktioniert das ... aber dazu muss man die Datei schlucken und alles auf einmal analysieren. Dies funktioniert in einem Stream - er kann Eingaben jeder Größe verarbeiten, sofern die Zeilen nicht astronomisch lang sind. Er druckt , wie es funktioniert - und es analysiert alle man‚s \backslash Fluchten zu booten. Aber es manist nur eine einzige Anwendung - ich habe viel davon auch auf andere Probleme angewendet ...
mikeserv
1
Ich ziehe nur an deiner Kette, weil ich mit einem winzigen Liner machen kann, was du beschreibst. Beachten Sie jedoch, dass es nicht die gesamte Datei verschluckt, sondern in einem Stream funktioniert. Es definiert nur „Linien“ können \n\nstatt , \naber immer noch jede Größe Eingang verarbeiten können und druckt wie es funktioniert. Siehe " Absatzmodus
terdon
@terdon Vielleicht wäre das hier ein besserer Weg gewesen. In sedihm kann wie geschehen: '/./{H;$!d' -e '};x;now work the paragraph...'. Das mache ich auch oft. Aber ich habe ursprünglich den ersten Teil geschrieben, in dem ich ein Protokoll unbegrenzt lange live gesehen habe, und selbst dieses Verhalten war zweifelhaft - der Puffer kann unter bestimmten Bedingungen explodieren. Das war nur halb so groß - manmachte es schwerer. Ich habe man -Hnach der manobigen Zusammenfassung nachgesehen und denke, es könnte einfacher sein, mit dem maschinengeschriebenen HTML-Code zu arbeiten, den groff auf GNU-Systemen drucken kann. Ich bin schon ein bisschen bogentief
mikeserv
@terdon - Ich habe mich selbst nachgedacht und einen absatzorientierten Ansatz versucht, aber es ist einfacher als es ist. Dies erhält Abschnitte. Like mansed cmd DESCRIPTIONgets DESCRIPTION section - und alle enthaltenen. Eine übereinstimmende Suche wird als Ganzes gedruckt und als ob ihre Einrückungsstufe die oberste war. Es werden sogar falsche Positive übersprungen, indem übereinstimmende Absätze ignoriert, aber nicht weiter eingerückt werden. Es passt seine Argumente an, indem die Farbe entweicht und diese nicht verarbeitet, bis es definitiv bereit ist, eine Zeile zu drucken. All das fällt mir sehr schwer, mit viel mehr Daten als einer einzelnen Zeile gleichzeitig fertig zu werden.
mikeserv
1

Jede Shell hat ihre eigenen Buildins. Es gibt zwar Gemeinsamkeiten, aber jede hat ihre eigenen Besonderheiten, die dokumentiert werden müssen.

Auf Systemen wie Linux und FreeBSD (und OSX, das von FreeBSD erbt), auf denen jede Shell als separates Paket bereitgestellt wird, gibt es keine Manpage für Builtins. Stattdessen wird jedes eingebaute Element in der Manpage der Shell dokumentiert. Lesen Sie also die bash-Manpage für die Dokumentation von Bashs killBuiltin, die dash-Manpage für die Dokumentation von killDashs Builtin usw. Es gibt auch eine Manpage für das killStandalone-Dienstprogramm.

Siehe Kann ich einzelne Manpages für die in Bash eingebauten Befehle erhalten? für eine manFunktion, die die interne Dokumentation von bash anstelle der Manpage anzeigt, wenn das Argument der Name eines eingebauten Elements ist.

Es gibt Unix-Varianten, die Manpages für Shell-Buildins bereitstellen - in der Tat tun es die meisten kommerziellen Varianten. Dies ist möglich, da das System entweder mit einer einzelnen Shell oder einer Reihe bekannter Shells geliefert wird. In der Manpage werden die Unterschiede zwischen den Shells erläutert. Zum Beispiel kann die fg(1)Manpage auf Solaris 10 hat Abschnitte für sh, kshund csh. Die fg(1)Manpage unter AIX 7.1 verweist auf „Korn-Shell“ und „POSIX-Shell“, erläutert sie jedoch gemeinsam (sie unterstützen zufällig genau dieselben Funktionen für fg). In der fg(1)Manpage zu Tru64 5.0 werden die in ksh integrierten Funktionen erläutert und csh-Benutzer auf die csh(1)Manpage verwiesen . SCOanscheinend kommt mit einer einzigen schale. Sie können andere Shells als Add-On-Pakete auf diesen Betriebssystemen installieren. Wenn Sie eine benutzerdefinierte Shell verwenden, müssen Sie beachten, dass die Manpages für Builtins nicht relevant sind, wenn Sie eine nicht standardmäßige Shell verwenden.

Gilles 'SO - hör auf böse zu sein'
quelle