Mengapa shell builtin tidak memiliki halaman manual yang benar?

32

Semua shell builtins berbagi halaman buku panduan yang sama:

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

NAME
     builtin, !

dll.

Lalu ada sedikit teks yang menggambarkan apa itu builtin shell, dan kemudian daftar yang terlihat seperti ini:

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

Tetapi jika kita lakukan man grepkita mendapatkan bagian seperti

  • Bug
  • Sejarah
  • Lihat juga
  • Standar
  • Deskripsi

dll.

Bukankah shell builtin memiliki sejarah, deskripsi, dan argumen sendiri seperti -Aatau -r? Mengapa itu tidak disediakan di halaman manual dan bagaimana saya belajar menggunakannya dengan benar dan efisien?

Nama tampilan
sumber

Jawaban:

25

Karena builtin adalah bagian dari shell. Bug atau riwayat apa pun yang mereka miliki adalah bug dan riwayat shell itu sendiri. Mereka bukan perintah independen dan tidak ada di luar shell yang dibangun ke dalamnya.

Setara, bashsetidaknya, adalah helpperintah. Sebagai contoh:

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

Semua bash builtin memiliki helphalaman. Bahkan helpitu sendiri:

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

Terinspirasi oleh sedskrip @ mikeserv , berikut adalah beberapa fungsi yang akan mencetak bagian yang relevan dari halaman manual menggunakan Perl. Tambahkan baris ini ke file inisialisasi shell Anda ( ~/.bashrcuntuk bash):

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

Kemudian, Anda menjalankannya dengan memberikan halaman manual dan nama bagian:

$ 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
sumber
2
@DisplayName mereka adalah bash. Mereka adalah bagian dari itu dan ya, mereka dijelaskan di SHELL BUILTIN COMMANDSbagian bashhalaman manual. "Halaman manual" mereka adalah help builtin_name.
terdon
3
Yang tidak jelas adalah mengapa mereka tidak diberi halaman manual. Halaman manual hanyalah file di MANPATH. Mereka tidak harus berhubungan dengan biner yang terpisah. Pada prinsipnya tidak ada alasan mengapa bash tidak dapat mengirim halaman manual untuk bawaannya - daripada memiliki sistem bantuan internal.
Francis Davey
4
@ Franciscavey: Tetapi sebagian besar builtin ada (dengan ekstensi berbeda) di berbagai shell. Halaman manual tidak khusus untuk shell; mereka di seluruh sistem.
rici
2
@ Franciscavey Seperti kata Rici, perintahnya tidak lebar sistem. Ini akan menjadi sedikit menyesatkan untuk memiliki halaman manual untuk perintah yang tidak hadir dalam setiap shell, tetapi lebih buruk lagi, itu akan sangat membingungkan untuk memiliki halaman manual untuk perintah yang adalah hadir dalam beberapa kerang, tetapi yang berperilaku berbeda (misalnya , menerima argumen yang berbeda, memiliki sintaks yang berbeda, dll.).
Joshua Taylor
1
@ mikeserv Namun, saya akan menyambut halaman manual untuk builtin shell di sepanjang garis apa, misalnya, tawaran git, di mana man git commitmenampilkan halaman manual untuk git-commit. Sesuatu seperti man bash ifitu akan luar biasa .
Joshua Taylor
5

Meskipun memang benar bahwa beberapa builtin shell mungkin memiliki sedikit menunjukkan dalam manual lengkap - terutama untuk bashbuiltin spesifik yang Anda hanya akan menggunakan pada sistem GNU (orang-orang GNU, sebagai aturan, sebagai aturan, tidak percaya pada mandan lebih suka infohalaman mereka sendiri ) - sebagian besar utilitas POSIX - shell builtin atau lainnya - sangat terwakili dalam Panduan Programmer POSIX.

Berikut adalah kutipan dari bagian bawah saya man sh (yang mungkin sekitar 20 halaman atau lebih ...)

masukkan deskripsi gambar di sini

Semua orang yang ada, dan lain-lain tidak disebutkan seperti set, read, break... baik, saya tidak perlu nama mereka semua. Tetapi perhatikan (1P)bagian kanan bawah - ini menunjukkan seri manual kategori 1 POSIX - itu adalah manhalaman yang saya bicarakan.

Mungkin Anda hanya perlu menginstal paket? Ini terlihat menjanjikan untuk sistem Debian. Meskipun helpberguna, jika Anda dapat menemukannya, Anda pasti harus mendapatkan POSIX Programmer's Guideseri itu. Ini bisa sangat membantu. Dan halaman konstituennya sangat detail.

Selain itu, shell builtin hampir selalu terdaftar di bagian spesifik dari manual shell tertentu. zsh, misalnya, memiliki seluruh manhalaman terpisah untuk itu - (Saya pikir totalnya 8 atau 9 zshhalaman individu - termasuk zshallyang sangat besar.)

Anda grep mantentu saja dapat:

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

... yang cukup dekat dengan apa yang saya lakukan ketika mencari manhalaman shell . Tetapi helpcukup bagus bashdalam banyak kasus.

Saya sebenarnya telah mengerjakan sedskrip untuk menangani hal-hal semacam ini baru-baru ini. Begitulah cara saya meraih bagian pada gambar di atas. Ini masih lebih lama dari yang saya suka, tetapi membaik - dan bisa sangat berguna. Dalam iterasi saat ini ia akan cukup andal mengekstrak bagian teks konteks-sensitif sebagai dicocokkan dengan bagian atau judul ayat berdasarkan pada pola [a] yang diberikan pada baris perintah. Ini warna output dan mencetak ke stdout.

Ini bekerja dengan mengevaluasi level indentasi. Jalur input non-kosong umumnya diabaikan, tetapi ketika bertemu dengan garis kosong, ia mulai memperhatikan. Ia mengumpulkan garis-garis dari sana sampai ia memverifikasi bahwa urutan saat ini jelas membuat indentasi lebih dalam daripada yang dilakukan baris pertama sebelum baris kosong lainnya terjadi atau ia menjatuhkan thread dan menunggu untuk kosong berikutnya. Jika tes ini berhasil, ia mencoba untuk mencocokkan garis memimpin dengan args baris perintahnya.

Ini berarti bahwa pertandingan pola akan cocok:

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

..dan..

match
   text

..tapi tidak..

heading
    match
    match

    notmatch

..atau..

         text

         match
         match
         text

         more text

Jika kecocokan bisa didapat maka mulai mencetak. Ini akan menghapus kosong baris terkemuka yang cocok dari semua baris yang dicetaknya - jadi tidak peduli level indent yang ditemukannya, garis itu mencetaknya seolah-olah berada di atas. Ia akan terus mencetak hingga menemukan baris lain pada level indentasi yang sama atau kurang dari garis yang cocok - sehingga seluruh bagian diambil hanya dengan korek api heading, termasuk semua / semua sub-bagian, paragraf yang mungkin dikandungnya.

Jadi pada dasarnya jika Anda memintanya untuk mencocokkan suatu pola, ia hanya akan melakukannya terhadap judul subjek dan akan mewarnai dan mencetak semua teks yang ditemukannya di dalam bagian yang dikepalai oleh kecocokannya. Tidak ada yang disimpan karena ia melakukan ini kecuali indentasi baris pertama Anda - dan karenanya bisa sangat cepat dan menangani \ninput yang dipisahkan secara ewline untuk hampir semua ukuran.

Butuh beberapa saat bagi saya untuk mengetahui cara berulang ke subpos seperti berikut:

Section Heading
    Subsection Heading

Tapi saya akhirnya mengatasinya.

Saya memang harus mengerjakan ulang semuanya demi kesederhanaan. Sementara sebelum saya memiliki beberapa loop kecil melakukan sebagian besar hal yang sama dengan cara yang sedikit berbeda agar sesuai dengan konteks mereka, dengan memvariasikan cara rekursi mereka, saya berhasil menduplikasi sebagian besar kode. Sekarang ada dua loop - satu cetakan dan satu cek indentasi. Keduanya tergantung pada tes yang sama - loop cetak dimulai ketika tes lulus dan loop indentasi mengambil alih ketika gagal atau dimulai pada baris kosong.

Seluruh proses ini sangat cepat karena sebagian besar waktu hanya /./dmenghapus garis yang tidak kosong dan beralih ke yang berikutnya - bahkan hasil dari zshallmengisi layar secara instan. Ini belum berubah.

Bagaimanapun, ini sangat berguna sejauh ini. Sebagai contoh, readhal di atas dapat dilakukan seperti:

mansed bash read

... Dan itu mendapat seluruh blok. Itu bisa mengambil pola atau apa saja, atau beberapa argumen, meskipun yang pertama selalu manhalaman di mana ia harus mencari. Berikut adalah gambar dari beberapa output setelah saya melakukannya:

mansed bash read printf

masukkan deskripsi gambar di sini

... kedua blok dikembalikan utuh. Saya sering menggunakannya seperti:

mansed ksh '[Cc]ommand.*'

... yang ini cukup berguna. Juga, SYNOPS[ES]membuatnya sangat berguna:

masukkan deskripsi gambar di sini

Ini dia jika Anda ingin berputar - saya tidak akan menyalahkan Anda jika Anda tidak melakukannya.

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

Secara singkat, alur kerjanya adalah:

  • baris apa pun yang tidak kosong dan yang tidak mengandung \nkarakter ewline dihapus dari output.
    • \nkarakter garis tidak pernah terjadi dalam ruang pola input. Mereka hanya dapat dimiliki sebagai hasil edit.
  • :printdan :indentkeduanya merupakan loop tertutup yang saling bergantung dan merupakan satu-satunya cara untuk mendapatkan \newline.
    • :printSiklus lingkaran dimulai jika karakter utama pada suatu baris adalah serangkaian kosong diikuti oleh \nkarakter ewline.
    • :indentSiklus dimulai pada baris kosong - atau pada :printbaris siklus yang gagal #test- tetapi :indentmenghapus semua \nurutan baris + kosong awal dari outputnya.
    • begitu :printdimulai, ia akan terus menarik garis input, mengosongkan spasi putih hingga jumlah yang ditemukan pada baris pertama dalam siklusnya, menerjemahkan overstrike dan understrike backspace lolos ke terminal warna lolos, dan mencetak hasilnya hingga #testgagal.
    • sebelum :indentmulai, pertama-tama periksa hruang lama untuk kemungkinan kelanjutan inden yang mungkin (seperti Subbagian) , dan kemudian terus menarik input selama #testgagal dan setiap baris setelah yang pertama terus cocok [-. Ketika sebuah baris setelah yang pertama tidak cocok dengan pola itu dihapus - dan kemudian semua baris berikut sampai baris kosong berikutnya.
  • #matchdan #testmenjembatani dua loop tertutup.
    • #testlewat ketika deretan kosong depan lebih pendek dari seri diikuti oleh baris terakhir \ndalam urutan garis.
    • #matchmenambahkan beberapa baris utama yang \ndiperlukan untuk memulai :printsiklus ke :indenturutan output mana pun yang mengarah pada kecocokan dengan setiap baris perintah. Urutan yang tidak dirender kosong - dan garis kosong yang dihasilkan dilewatkan kembali ke :indent.
mikeserv
sumber
2
Sed-fu Anda kuat. Tentu saja, Anda dapat melakukan hal yang sama dengan manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }dan kemudian manperl sh SYNOPSISatau manperl sh read:)
terdon
@terdon - tidak, Anda tidak bisa. Ini tidak memakan input. Saya bisa melakukan hal yang sama seperti itu sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'... mungkin itu bekerja ... tetapi itu membutuhkan menelan file dan menguraikan semuanya sekaligus. Ini berfungsi dalam aliran - ia dapat menangani input dengan ukuran berapa pun asalkan garisnya tidak terlalu panjang. Mencetak karena bekerja - dan mem-parsing semua man's \backslash lolos boot. Tetapi manhanya satu aplikasi untuk itu - Saya telah menerapkan sebagian besar untuk masalah lain juga ...
mikeserv
1
Saya hanya menarik rantai Anda karena saya bisa melakukan apa yang Anda gambarkan dengan liner kecil. Perhatikan, bagaimanapun, bahwa itu tidak menelan keseluruhan file, ia bekerja dalam aliran. Itu hanya mendefinisikan "garis" menggunakan \n\nbukan \ntetapi masih dapat menangani input ukuran dan cetak saat bekerja. Lihat "mode paragraf" di sini: perldoc.perl.org/perlrun.html
terdon
@terdon Mungkin itu cara yang lebih baik untuk pergi ke sini. Dalam sedhal itu dapat dilakukan seperti: '/./{H;$!d' -e '};x;now work the paragraph...'. Saya juga sering melakukan itu. Tapi saya awalnya menulis bagian pertama untuk menonton log secara langsung untuk waktu yang tidak terbatas, dan bahkan perilaku itu rapuh - buffer dapat meledak dalam kondisi tertentu. Itu hanya setengah dari ukuran ini - manmembuatnya lebih sulit. Saya melihat man -Hsetelah mendapatkan mansynop di atas, dan saya pikir mungkin lebih mudah untuk bekerja dengan HTML genetika mesin yang dapat dicetak groff pada sistem GNU. Saya sudah sedalam siku
mikeserv
@terdon - Saya menebak-nebak diri saya dan mencoba pendekatan yang berpusat pada paragraf, tetapi lebih mudah seperti itu. Ini mendapat bagian. Seperti mansed cmd DESCRIPTIONmendapat bagian DESCRIPTION - dan semua yang termasuk. Pencarian yang cocok dicetak seluruhnya dan seolah-olah level indentanya berada di atas. Bahkan melompati positif palsu dengan mengabaikan paragraf yang cocok tetapi kemudian tidak membuat indentasi lebih lanjut. Ini cocok dengan argumennya melalui backspace warna yang lolos dan tidak mengatasinya sampai siap untuk mencetak garis. Semua itu sangat sulit bagi saya untuk dilakukan dengan lebih banyak data daripada satu baris sekaligus.
mikeserv
1

Setiap shell memiliki set builtin sendiri. Meskipun ada kesamaan, mereka masing-masing memiliki kekhasan masing-masing yang perlu didokumentasikan.

Pada sistem seperti Linux dan FreeBSD (dan OSX, yang mewarisi dari FreeBSD) di mana setiap shell disediakan sebagai paket terpisah, tidak ada halaman manual untuk builtin; sebaliknya, masing-masing builtin didokumentasikan dalam halaman manual shell. Jadi bacalah halaman manual bash untuk dokumentasi bash's killbuiltin, bacalah halaman manual dasbor untuk dokumentasi dasbor bash kill, dll. Ada juga halaman manual untuk killutilitas mandiri.

Lihat Bisakah saya mendapatkan halaman manual untuk perintah bash builtin? untuk manfungsi yang memperlihatkan dokumentasi internal bash alih-alih halaman manual jika argumennya adalah nama builtin.

Ada varian unix yang menyediakan halaman manual untuk builtin shell - pada kenyataannya, sebagian besar varian komersial melakukannya. Itu layak karena sistem dilengkapi dengan satu shell, atau satu set shell yang dikenal. Halaman manual membahas perbedaan antara shell. Sebagai contoh, fg(1)halaman manual pada Solaris 10 memiliki bagian untuk sh, kshdan csh. The fg(1)man halaman di AIX 7.1 referensi “Korn shell” dan “POSIX shell” tapi membahas mereka bersama-sama (mereka terjadi untuk mendukung fitur yang sama persis untuk fg). The fg(1)man halaman di Tru64 5.0 membahas yang ksh builtin dan mengacu pengguna csh ke csh(1)halaman manual. SCORupanya datang dengan satu shell. Anda dapat menginstal shell lain sebagai paket tambahan pada sistem operasi ini; jika Anda menggunakan shell khusus, Anda harus ingat bahwa halaman manual untuk builtin tidak akan relevan ketika menggunakan shell non-default.

Gilles 'SANGAT berhenti menjadi jahat'
sumber