Perché i built-in della shell non hanno pagine man appropriate?


32

Tutti i builtin della shell condividono la stessa pagina di manuale:

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

NAME
     builtin, !

eccetera.

Quindi c'è un piccolo testo che descrive cosa sono i builtin della shell, e quindi un elenco che assomiglia a questo:

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

Ma se lo facciamo man grepotteniamo sezioni come

  • bugs
  • Storia
  • Guarda anche
  • Standards
  • Descrizione

eccetera.

I builtin della shell non hanno la loro storia, descrizione e argomenti come -Ao -r? Perché non viene fornito nelle pagine del manuale e come dovrei imparare ad usarli correttamente ed efficientemente?


Risposte:


25

Perché i builtin fanno parte della shell. Qualsiasi bug o cronologia che hanno sono bug e cronologia della shell stessa. Non sono comandi indipendenti e non esistono al di fuori della shell in cui sono integrati.

L'equivalente, bashalmeno, è il helpcomando. Per esempio:

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

Tutti i built-in bash hanno helppagine. Anche helpse stesso:

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

Ispirato allo sedscript di @ mikeserv , ecco una piccola funzione che stamperà la sezione pertinente di una pagina man usando Perl. Aggiungi questa riga al file di inizializzazione della tua shell ( ~/.bashrcper bash):

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

Quindi, lo esegui dandogli una pagina man e il nome di una sezione:

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

2
@DisplayName sono bash. Ne fanno parte e sì, sono spiegati nella SHELL BUILTIN COMMANDSsezione della bashpagina man. Le loro "pagine man" sono help builtin_name.
terdon

3
Ciò che non è chiaro è perché non hanno ricevuto pagine man. Le pagine man sono solo file sul MANPATH. Non devono corrispondere a binari separati. In linea di principio non c'è motivo per cui bash non avrebbe potuto essere fornito con le pagine man per i suoi builtin - piuttosto che avere un sistema di aiuto interno.
Francis Davey,

4
@FrancisDavey: Ma la maggior parte dei builtin esiste (con estensioni diverse) in varie shell. Le pagine man non sono specifiche della shell; sono a livello di sistema.
rici,

2
@FrancisDavey Come detto da rici, i comandi non sono a livello di sistema. Sarebbe un po 'fuorviante avere una manpage per un comando che non è presente in ogni shell, ma ancora peggio, sarebbe molto confuso avere una man page per un comando che è presente in più shell, ma che si comporta diversamente (es. , accetta argomenti diversi, ha sintassi diversa, ecc.).
Joshua Taylor,

1
@mikeserv Comunque, darei il benvenuto alle pagine man per i builtin della shell lungo le linee di ciò che, ad esempio, offre git, per cui viene man git commitvisualizzata una pagina man git-commit. Qualcosa del genere man bash ifsarebbe meraviglioso .
Joshua Taylor,

5

Sebbene sia vero che alcuni builtin della shell potrebbero avere una scarsa visualizzazione in un manuale completo, specialmente per quei bashbuiltin specifici che è probabile che tu utilizzi solo su un sistema GNU (la gente GNU, di regola, non crede mane preferiscono le proprie infopagine) - la stragrande maggioranza delle utility POSIX - shell incorporate o altro - sono ben rappresentate nella Guida del programmatore POSIX.

Ecco un estratto dal fondo del mio man sh (che probabilmente è lungo circa 20 pagine ...)

inserisci qui la descrizione dell'immagine

Tutti questi sono lì, e gli altri non menzionati, come set, read, break... beh, non ho bisogno di nominarli tutti. Ma nota (1P)in basso a destra - indica la serie di manuali POSIX categoria 1 - quelle sono le manpagine di cui sto parlando.

Potrebbe essere che hai solo bisogno di installare un pacchetto? Questo sembra essere molto promettente per un sistema Debian. Sebbene helpsia utile, se riesci a trovarlo, dovresti assolutamente ottenere quella POSIX Programmer's Guideserie. Può essere estremamente utile. E le sue pagine costitutive sono molto dettagliate.

A parte questo, i builtin della shell sono quasi sempre elencati in una sezione specifica del manuale specifico della shell. zsh, per esempio, ha un'intera manpagina separata per questo - (penso che ammonti a 8 o 9 o più zshpagine singole - incluso zshallche è enorme.)

grep manOvviamente puoi semplicemente :

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

... che è abbastanza vicino a quello che facevo quando cercavo una manpagina di shell . Ma helpè abbastanza buono bashnella maggior parte dei casi.

Ho lavorato a una sedsceneggiatura per gestire questo tipo di cose di recente. È come ho afferrato la sezione nella foto sopra. È ancora più lungo di quanto mi piaccia, ma sta migliorando - e può essere molto utile. Nella sua attuale iterazione estrarrà in modo abbastanza affidabile una sezione di testo sensibile al contesto abbinata a una sezione o un'intestazione di sottosezione basata su [a] pattern [s] forniti nella riga di comando. Colora il suo output e stampa su stdout.

Funziona valutando i livelli di rientro. Le righe di input non vuote vengono generalmente ignorate, ma quando incontra una riga vuota inizia a prestare attenzione. Raccoglie le linee da lì fino a quando non ha verificato che la sequenza corrente rientra decisamente più in profondità rispetto alla prima riga prima che si verifichi un'altra riga vuota, altrimenti lascia cadere il thread e attende il vuoto successivo. Se il test ha esito positivo, tenta di far corrispondere la linea guida ai suoi argomenti della riga di comando.

Ciò significa che una partita di pattern partita:

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

..e..

match
   text

..ma no..

heading
    match
    match

    notmatch

..o..

         text

         match
         match
         text

         more text

Se si può avere una corrispondenza, inizia a stampare. Spoglia gli spazi vuoti iniziali della linea abbinata da tutte le linee che stampa - quindi, indipendentemente dal livello di rientro che ha trovato quella linea su di essa, la stampa come se fosse in alto. Continuerà a stampare fino a quando non incontra un'altra riga a un livello uguale o inferiore al rientro rispetto alla riga corrispondente - quindi intere sezioni vengono acquisite con solo una corrispondenza di intestazione, inclusi eventuali sottotitoli, tutti i paragrafi che potrebbero contenere.

Quindi, fondamentalmente, se gli chiedi di abbinare uno schema, lo farà solo contro un titolo del soggetto di qualche tipo e colorerà e stamperà tutto il testo che trova nella sezione diretta dalla sua corrispondenza. Nulla viene salvato mentre lo fa tranne il rientro della prima riga, quindi può essere molto veloce e gestire \ninput separati da ewline praticamente di qualsiasi dimensione.

Mi ci è voluto un po 'per capire come ricorrere a sottotitoli come i seguenti:

Section Heading
    Subsection Heading

Ma alla fine l'ho risolto.

Ho dovuto rielaborare il tutto per semplicità, però. Mentre prima avevo diversi piccoli loop che facevano principalmente le stesse cose in modi leggermente diversi per adattarsi al loro contesto, variando i loro mezzi di ricorsione sono riuscito a de-duplicare la maggior parte del codice. Ora ci sono due anelli: uno stampa e uno rientra i segni di spunta. Entrambi dipendono dallo stesso test: il ciclo di stampa inizia quando il test viene superato e il ciclo di rientro subentra quando fallisce o inizia su una riga vuota.

L'intero processo è molto veloce perché la maggior parte delle volte /./delimina qualsiasi riga non vuota e passa alla successiva - anche i risultati di zshallpopolano lo schermo all'istante. Questo non è cambiato.

Comunque, finora è molto utile. Ad esempio, la readcosa sopra può essere fatta come:

mansed bash read

... e ottiene l'intero blocco. Può accettare qualsiasi modello o qualunque cosa, o più argomenti, sebbene il primo sia sempre la manpagina in cui dovrebbe cercare. Ecco una foto di alcuni dei suoi risultati dopo che l'ho fatto:

mansed bash read printf

inserisci qui la descrizione dell'immagine

... entrambi i blocchi vengono restituiti interi. Lo uso spesso come:

mansed ksh '[Cc]ommand.*'

... per il quale è abbastanza utile. Inoltre, ottenere lo SYNOPS[ES]rende davvero utile:

inserisci qui la descrizione dell'immagine

Eccolo se vuoi fare un giro - non ti biasimerò se non lo fai.

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

In breve, il flusso di lavoro è:

  • qualsiasi riga che non è vuota e che non contiene un \ncarattere di ewline viene eliminata dall'output.
    • \ni caratteri di ewline non si verificano mai nello spazio del motivo di input. Possono essere ottenuti solo come risultato di una modifica.
  • :printe :indentsono entrambi circuiti chiusi reciprocamente dipendenti e sono l'unico modo per ottenere una \newline.
    • :printIl ciclo del ciclo inizia se i caratteri \niniziali su una riga sono una serie di spazi vuoti seguiti da un carattere ewline.
    • :indentIl ciclo inizia su righe vuote - o su :printlinee cicliche che non riescono #test- ma :indentrimuove \ndal suo output tutte le sequenze principali di bianco + ewline.
    • una volta :printiniziato, continuerà a inserire le righe di input, rimuovere gli spazi bianchi iniziali fino alla quantità trovata sulla prima riga del suo ciclo, tradurre la sovraincisione e minimizzare le escape di backspace in escape di terminali di colore e stampare i risultati fino a quando #testnon riesce.
    • prima di :indentiniziare, controlla innanzitutto lo hspazio vecchio per l'eventuale continuazione del rientro (come una sottosezione) , quindi continua a inserire l'input fino a quando #testnon riesce e qualsiasi riga successiva alla prima continua a corrispondere [-. Quando una riga dopo la prima non corrisponde a quel modello, viene eliminata - e successivamente lo sono anche tutte le righe seguenti fino alla riga vuota successiva.
  • #matche #testgetti un ponte sui due anelli chiusi.
    • #testpassa quando la serie principale di spazi vuoti è più corta della serie seguita dall'ultima \newline in una sequenza di linee.
    • #matchantepone le \nlinee guida principali necessarie per iniziare un :printciclo a una qualsiasi delle :indentsequenze di output che conducono con una corrispondenza a qualsiasi arg della riga di comando. Quelle sequenze che non vengono rese vuote e la riga vuota risultante viene restituita a :indent.

2
Il tuo sed-fu è forte. Certo, puoi fare la stessa cosa con manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }e poi manperl sh SYNOPSISo manperl sh read:)
terdon

@terdon - no, non puoi. Questo non mangia input. Potrei fare la stessa cosa del genere sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'... probabilmente funziona ... ma ciò richiede di ingoiare il file e analizzarlo tutto in una volta. Funziona in un flusso: può gestire input di qualsiasi dimensione purché le linee non siano astronomicamente lunghe. Stampa mentre funziona e analizza tutte manle \bescape di ackslash per l'avvio. Ma manè solo una singola applicazione per questo - ne ho applicato gran parte anche ad altri problemi ...
mikeserv

1
Sto solo strappando la catena dal momento che posso fare quello che descrivi con una piccola fodera. Si noti, tuttavia, che non inghiotte l'intero file, funziona in un flusso. Definisce semplicemente "linee" utilizzando \n\ninvece di \nma può comunque gestire qualsiasi input di dimensioni e stampa mentre funziona. Vedere "Modalità paragrafo" qui: perldoc.perl.org/perlrun.html
terdon

@terdon Forse sarebbe stato un modo migliore di andare qui. In sedessa si può fare come: '/./{H;$!d' -e '};x;now work the paragraph...'. Lo faccio spesso anche io. Ma originariamente ho scritto la prima parte per guardare un registro dal vivo per un periodo di tempo illimitato, e anche quel comportamento era incerto: il buffer può esplodere in determinate condizioni. Questa era solo la metà di questa dimensione, manrendendola più difficile. Ho guardato man -Hdopo aver ottenuto il mansinodo qui sopra, e sto pensando che potrebbe essere più facile lavorare con l'HTML generato dalla macchina che groff può stampare su sistemi GNU. Sono un po 'gomito fino al gomito
mikeserv il

@terdon - Ho fatto una seconda ipotesi e ho provato un approccio centrato sul paragrafo, ma è più facile così com'è. Questo ottiene sezioni. Come mansed cmd DESCRIPTIONottiene la sezione DESCRIZIONE - e tutti quelli inclusi. Una ricerca corrispondente viene stampata intera e come se il suo livello di rientro fosse il primo. Salta persino i falsi positivi ignorando i paragrafi che corrispondono ma non rientrano ulteriormente. Abbina i suoi argomenti attraverso le fughe di backspace a colori e non li gestisce fino a quando non è sicuramente pronto per stampare una linea. Tutto ciò è molto difficile per me fare con molti più dati di una singola riga alla volta.
Mikeserv,

1

Ogni shell ha il proprio set di builtin. Sebbene esistano elementi comuni, ognuno ha le proprie peculiarità che devono essere documentate.

Su sistemi come Linux e FreeBSD (e OSX, che eredita da FreeBSD) in cui ogni shell è fornita come pacchetto separato, non esiste una pagina man per i builtin; invece, ogni built-in è documentato nella pagina man della shell. Quindi leggi la pagina man di bash per la documentazione del killbuiltin di bash , leggi la pagina man di dash per la documentazione del killbuilt-in di dash , ecc. C'è anche una pagina man per l' killutility standalone.

Vedi Posso ottenere singole pagine man per i comandi integrati bash? per una manfunzione che mostra la documentazione interna di bash invece della pagina man se l'argomento è il nome di un builtin.

Ci sono varianti unix che forniscono pagine man per i builtin della shell - in effetti, la maggior parte delle varianti commerciali lo fanno. Questo è fattibile perché il sistema viene fornito con una singola shell o con una serie di shell note. La pagina man discute le differenze tra le conchiglie. Ad esempio, la fg(1)pagina man su Solaris 10 contiene sezioni per sh, kshe csh. La fg(1)pagina man su AIX 7.1 fa riferimento a "Korn shell" e "POSIX shell" ma ne discute insieme (per caso supportano esattamente le stesse funzionalità fg). La fg(1)pagina man su Tru64 5.0 discute l'integrato di ksh e rimanda gli utenti csh alla csh(1)pagina man. SCOapparentemente viene fornito con una singola shell. È possibile installare altre shell come pacchetti aggiuntivi su questi sistemi operativi; se si utilizza una shell personalizzata, è necessario ricordare che le pagine man per i builtin non saranno rilevanti quando si utilizza una shell non predefinita.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.