为什么shell buildins没有正确的手册页?


32

所有的shell内置程序都共享相同的手册页:

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

NAME
     builtin, !

等等

然后是一小段文字,描述什么是shell内置程序,然后是一个看起来像这样的列表:

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

但是,如果这样做,man grep我们会得到诸如

  • 虫子
  • 历史
  • 也可以看看
  • 标准品
  • 描述

等等

Shell内置外壳程序没有自己的历史记录,描述和参数,例如-Aor -r吗?为什么手册页中没有提供这些信息,我将如何学习正确有效地使用它们?


Answers:


25

因为内建函数是外壳的一部分。他们拥有的任何错误或历史记录都是Shell本身的错误和历史记录。它们不是独立的命令,并且不存在于内置的外壳程序之外。

bash至少等价于help命令。例如:

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

所有bash内置文件都有help页面。甚至help本身:

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

受@mikeserv sed脚本的启发,以下是一个小功能,它将使用Perl打印手册页的相关部分。将此行添加到您的shell的初始化文件中(~/.bashrc用于bash):

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

然后,通过给它一个手册页和一个节的名称来运行它:

$ 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他们 bash。它们是其中的一部分,是的,它们SHELL BUILTIN COMMANDSbash手册页的部分中进行了说明。他们的“手册页”是help builtin_name
terdon

3
尚不清楚的是为什么没有为其提供手册页。手册页只是MANPATH上的文件。它们不必对应于单独的二进制文件。从原则上讲,没有理由为什么bash不能随其内置的手册一起提供-而不是具有内部帮助系统。
弗朗西斯·戴维2014年

4
@FrancisDavey:但是大多数内置插件存在(具有不同的扩展名)在各种外壳中。联机帮助页不是特定于shell的;它们是系统范围的。
rici 2014年

2
@FrancisDavey正如rici所说,命令不是系统范围的。如果没有在每个shell中都存在一个命令的手册页,那会产生误导,但更糟糕的是,对于在多个shell 中存在但行为不同的命令有一个手册页,这将非常令人困惑。,接受不同的参数,具有不同的语法等)。
约书亚·泰勒

1
@mikeserv但是,我按照git提供的内容欢迎shell内建程序的手册页,例如git会在其中man git commit提供手册页git-commit。喜欢的东西man bash if美好的
约书亚·泰勒

5

的确,某些shell内置程序在完整的手册中可能很少显示-尤其是对于那些bash特定的内置程序,您只可能在GNU系统上使用它们(通常,GNU人士不要相信man和偏好自己的info页面) -绝大多数POSIX实用程序(shell内置或其他)在POSIX程序员指南中都有很好的表示。

这是我底部的摘录man sh (可能长20页左右...)

在此处输入图片说明

所有这些都在那里,和其他人没有提到这样的setreadbreak...好,我不需要给它们命名所有。但是请注意(1P)右下角的-它表示POSIX 1类手册系列-这些是man我正在谈论的页面。

可能只是您需要安装软件包?对于Debian系统,看起来很有希望。虽然help很有用,但是如果可以找到它,则一定要获得该POSIX Programmer's Guide系列文章。这可能非常有帮助。它的组成页面非常详细。

除此之外,shell内置文件几乎总是在特定的shell手册的特定部分中列出。zsh,例如,有一个完整的单独man页面- (我认为总共有8或9个左右的zsh页面-包括zshall很大的页面。)

grep man当然可以:

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

...这与我在搜索Shell man页面时所做的非常接近。但是在大多数情况下,这help是相当不错的bash

实际上,我最近一直在sed处理脚本来处理这种事情。这就是我抓取上图中的部分的方式。它的长度比我喜欢的还要长,但是它正在改进-可以非常方便。在当前的迭代中,它将基于命令行中给定的[a]模式,非常可靠地提取与上下文相关的文本节,该文本与节或小节标题匹配。它为输出着色并打印到标准输出。

它通过评估缩进级别来工作。非空白输入行通常被忽略,但是当遇到空白行时,它开始引起注意。它从那里开始收集行,直到确认当前序列肯定比第一行缩进为止,然后再出现另一条空行,否则它将丢弃线程并等待下一个空行。如果测试成功,它将尝试将引线与其命令行参数匹配。

这意味着匹配模式将匹配:

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

..和..

match
   text

..但不是..

heading
    match
    match

    notmatch

..要么..

         text

         match
         match
         text

         more text

如果可以进行匹配,它将开始打印。它将从打印的所有行中去除匹配行的前导空白-因此,无论缩进级别是多少,只要发现它在该行上的行都像打印在顶部一样,就可以缩进。它会继续打印,直到遇到与匹配行相等或小于缩进级别的另一行为止-因此,整个节仅以标题匹配项(包括任何/所有小节及其可能包含的段落)进行抓取。

因此,基本上,如果您要求它匹配某个模式,它只会针对某种主题标题进行匹配,并且会对其匹配的标题部分中找到的所有文本进行着色和打印。除了第一行的缩进以外,什么也不会保存,因此它可以非常快地处理\n几乎任何大小的单行分隔输入。

我花了一段时间才弄清楚如何递归到以下子标题:

Section Heading
    Subsection Heading

但我最终对其进行了整理。

不过,为了简单起见,我确实必须对整个过程进行重新设计。在我有几个小循环以适应它们的上下文的方式以略有不同的方式执行大多数相同的事情之前,通过改变其递归方式,我设法对大多数代码进行了重复数据删除。现在有两个循环-一个打印和一个检查缩进。两者都依赖于同一测试-打印循环在测试通过时开始,缩进循环在测试失败或从空白行开始时接管。

整个过程非常快,因为在大多数情况下,它只会/./d清除任何非空白行并移至下一行-即使zshall是立即填充屏幕也会导致结果。这没有改变。

无论如何,到目前为止,它还是非常有用的。例如,read上面的事情可以这样完成:

mansed bash read

...并获得整个区块。它可以采用任何模式或其他方式,也可以采用多个参数,尽管第一个始终是man它应该在其中搜索的页面。这是我做完之后的一些输出图片:

mansed bash read printf

在此处输入图片说明

...两个块都全部返回。我经常像这样使用它:

mansed ksh '[Cc]ommand.*'

...这是非常有用的。另外,获取SYNOPS[ES]变得非常方便:

在此处输入图片说明

在这里,如果您想旋转一下-如果您不这样做,我不会怪您。

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

简而言之,工作流程是:

  • \n从输出中删除所有非空白且不包含尾行字符的行。
    • \n在输入模式空间中永远不会出现行字符。它们只能作为编辑的结果。
  • :print:indent都是相互依赖的闭环,并且是获得\n直线的唯一方法。
    • :print如果一行上的前导字符是一系列空格后跟一个\newline字符,则开始循环循环。
    • :indent的循环从空白行开始-或在:print失败的循环行上开始#test-但:indent\n从其输出中删除所有开头的空白+ ewline序列。
    • 一旦:print开始,它将继续拉入输入行,将前导空白删除到其循环中第一行所找到的数量,将过分删除和欠删除退格转义转换为彩色终端转义,并打印结果直到#test失败。
    • :indent开始之前,它首先检查h旧空间是否有任何可能的缩进延续(例如Subsection),然后只要#test失败就继续输入,并且第一个之后的任何行都继续匹配[-。当第一行之后的行与该模式不匹配时,将其删除-随后,所有后续行也将删除,直到下一个空白行。
  • #match#test桥接两个闭环。
    • #test当空白行的前导序列比\n行序列中的最后一条ewline短时,将通过。
    • #match\n开始:print循环所需的前导行添加到任何:indent与任何命令行arg匹配的输出序列中。那些不会变成空的序列-产生的空行被传回:indent

2
您的sed-fu很强壮。当然,您可以使用manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }and然后manperl sh SYNOPSISmanperl sh read:) 做相同的事情
terdon

@terdon-不,你不能。这不会吃输入。我可以做与sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'...可能相同的事情……可能有效……但是这需要吞下文件并立即解析所有文件。这可以在流中工作-如果线路在天文上不是很长,它可以处理任何大小的输入。它会在工作时进行打印-并解析所有man\bA斜线转义符以启动。但这man只是一个单一的应用程序-我也将其应用到其他许多问题上……
mikeserv 2014年

1
我只是在拖你的连锁店,因为我可以用一个很小的衬里就能做到你所描述的。但是请注意,它不会吞没整个文件,而是在流中工作。它只是使用\n\n而不是来定义“行” ,\n但仍可以处理任何大小的输入并在其工作时进行打印。请参阅此处的“段落模式”:perldoc.perl.org/perlrun.html
terdon

@terdon也许那是去这里的更好的方法。在sed其中可以完成以下操作:'/./{H;$!d' -e '};x;now work the paragraph...'。我也经常这样做。但是我最初写的第一部分是在无限制的时间内观看日志,即使这种行为也很不稳定-缓冲区在某些条件下可能会爆炸。那只是这个大小的一半- man变得更难了。不过,我在看完上面的摘要man -H后看了一下man,我认为使用groff可以在GNU系统上打印的机器生成的HTML可能会更容易。我有点弯头,已经很深了
mikeserv 2014年

@terdon-我进行了自我猜测,并尝试了一种以段落为中心的方法,但是这样更容易。这得到部分。像mansed cmd DESCRIPTION获取说明部分-以及所有包含的内容。匹配的搜索被完整打印,好像其缩进级别是第一位。它甚至通过忽略匹配但不进一步缩进的段落来跳过误报。它通过颜色退格转义符匹配其args 直到确定准备打印行时才对其进行处理。对于我而言,一次处理所有数据要比单行多得多,所有这些都非常困难。
mikeserv

1

每个外壳都有自己的一组内建函数。尽管存在共性,但它们各自都有各自的特点,需要记录在案。

在诸如Linux和FreeBSD(以及从FreeBSD继承而来的OSX)之类的系统上,每个shell作为单独的软件包提供,没有内置插件的手册页。相反,每个内置文件都记录在外壳的手册页中。因此,请阅读bash手册页以了解bash的kill内置文档,请阅读dash手册页以了解dash的kill内置文档,等等。还有一个kill独立实用程序的手册页。

请参见我可以获取bash内置命令的单独手册页吗?对于一个man功能展示bash的内部文件,而不是男人页面,如果该参数是一个内置的名字。

有unix变体为shell内置程序提供手册页-实际上,大多数商业变体都提供。这是可行的,因为系统附带一个外壳程序或一组已知外壳程序。手册页讨论了壳之间的区别。例如,fg(1)在Solaris 10手册页有部分shkshcshfg(1)AIX 7.1手册页引用了“ Korn shell”和“ POSIX shell”,但一起进行了讨论(它们恰好支持完全相同的功能fg)。fg(1)Tru64 5.0上手册页讨论了ksh内置功能,并将csh用户引至csh(1)手册页。上合组织显然带有单个外壳。您可以在这些操作系统上安装其他Shell作为附加软件包。如果使用自定义外壳程序,则必须记住,使用非默认外壳程序时,内置插件的手册页将不相关。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.