Почему встроенные команды оболочки не имеют надлежащих man-страниц?

32

Все встроенные оболочки имеют одну и ту же страницу руководства:

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

NAME
     builtin, !

и т.п.

Затем есть небольшой текст, описывающий встроенные функции оболочки, а затем список, который выглядит примерно так:

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

Но если мы делаем, man grepмы получаем такие разделы, как

  • ошибки
  • история
  • Смотрите также
  • стандарты
  • Описание

и т.п.

Не имеют ли встроенные команды оболочки свою историю, описание и аргументы типа -Aили -r? Почему это не предусмотрено на страницах руководства и как я могу научиться использовать их правильно и эффективно?

Отображаемое имя
источник

Ответы:

25

Потому что встроенные функции являются частью оболочки. Любые ошибки или история, которую они имеют, являются ошибками и историей самой оболочки. Они не являются независимыми командами и не существуют вне оболочки, в которую они встроены.

Эквивалентом, по 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. Добавьте эту строку в файл инициализации вашей оболочки ( ~/.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 они являются Баш. Они являются частью этого, и да, они объяснены в SHELL BUILTIN COMMANDSразделе bashсправочной страницы. Их "man-страницы" есть help builtin_name.
Terdon
3
Что не ясно, так это то, почему им не дали справочные страницы. Man-страницы - это просто файлы на MANPATH. Они не должны соответствовать отдельным двоичным файлам. В принципе нет причин, по которым bash не мог бы отправлять справочные страницы для своих встроенных функций, а не иметь внутреннюю справочную систему.
Фрэнсис Дейви
4
@FrancisDavey: Но большинство встроенных функций существуют (с разными расширениями) в различных оболочках. Manpages не являются специфичными для оболочки; они общесистемные.
Ричи
2
@FrancisDavey Как говорила Ричи, команды не являются общесистемными. Было бы немного вводит в заблуждение , чтобы иметь справочную страницу для команды, это не присутствует в каждой оболочке, но еще хуже, это было бы очень запутанным , чтобы иметь справочную страницу для команды, которая присутствует в нескольких оболочек, но ведет себя по- разному (например , , принимает разные аргументы, имеет другой синтаксис и т. д.).
Джошуа Тейлор
1
@mikeserv Тем не менее, я бы приветствовал страницы руководства для встроенных командных оболочек в соответствии с тем, что, например, предлагает git, где man git commitвызывается страница руководства git-commit. Нечто подобное man bash ifбыло бы замечательно .
Джошуа Тейлор
5

Хотя это правда, что некоторые встроенные оболочки могут иметь скудное отображение в полном руководстве - особенно для тех bashспецифических встроенных функций, которые вы, скорее всего, будете использовать только в системе GNU (люди GNU, как правило, не верят manи предпочитают свои собственные infoстраницы) - подавляющее большинство утилит POSIX - встроенных в оболочку или других - очень хорошо представлены в Руководстве программиста POSIX.

Вот отрывок из нижней части моего man sh (который, вероятно, 20 страниц или около того ...)

введите описание изображения здесь

Все те , есть и другие , не упомянутые , такие как set, read, break... ну, мне не нужно , чтобы назвать их всех. Но обратите внимание (1P)на нижний правый угол - он обозначает серию руководств по POSIX категории 1 - это manстраницы, о которых я говорю.

Может быть, вам просто нужно установить пакет? Это выглядит многообещающе для системы Debian. Хотя helpэто полезно, если вы можете найти его, вы обязательно должны получить эту POSIX Programmer's Guideсерию. Это может быть чрезвычайно полезно. И это составляющие страницы очень подробны.

Кроме того, встроенные функции оболочки почти всегда перечислены в определенном разделе руководства по конкретной оболочке. 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

... что довольно близко к тому, что я делал при поиске на manстранице оболочки . Но в большинстве случаев helpэто довольно хорошо bash.

Я на самом деле работал над sedсценарием, чтобы справиться с такими вещами в последнее время. Вот как я схватил раздел на картинке выше. Это все еще дольше, чем мне нравится, но оно улучшается - и может быть довольно удобным. В своей текущей итерации он довольно надежно извлекает контекстно-зависимый раздел текста в соответствии с заголовком раздела или подраздела на основе [a] pattern [s], заданного им в командной строке. Он окрашивает свой вывод и печатает на стандартный вывод.

Это работает, оценивая уровни отступа. Непустые строки ввода обычно игнорируются, но когда он встречает пустую строку, он начинает обращать внимание. Он собирает строки оттуда до тех пор, пока не проверит, что текущая последовательность определенно отступает дальше, чем ее первая строка, прежде чем появится другая пустая строка, или же он отбрасывает поток и ждет следующего пробела. Если тест пройден успешно, он пытается сопоставить ведущую строку со своими аргументами командной строки.

Это означает , что матч шаблон будет соответствовать:

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символа ewline, удаляется из вывода.
    • \nсимволы ewline никогда не появляются в пространстве входных паттернов. Их можно получить только в результате редактирования.
  • :printи :indentоба являются взаимозависимыми замкнутыми циклами и являются единственным способом получить электронную \nлинию.
    • :printЦикл цикла начинается, если ведущие символы в строке представляют собой серию пробелов, за которыми \nследует символ ewline.
    • :indentЦикл начинается на пустых строках - или на :printстроках цикла, которые терпят неудачу #test- но :indentудаляет все \nначальные пустые + электронные строки из своего вывода
    • как только :printон начнется, он продолжит вытягивать входные строки, обрезать начальные пробелы до величины, найденной в первой строке своего цикла, преобразовывать экранирование избыточного удара и обратного удара обратного пробела в побеги цветных терминалов и печатать результаты до тех пор, пока #testне произойдет сбой.
    • перед началом :indentон сначала проверяет hстарое пространство на предмет возможного продолжения отступа (такого как подраздел) , а затем продолжает вводить данные до тех пор, пока происходит #testсбой, и любая строка, следующая за первой, продолжает совпадать [-. Если строка после первой не совпадает с этим шаблоном, она удаляется - и впоследствии все последующие строки до следующей пустой строки.
  • #matchи #testсоединить две замкнутые петли.
    • #testпроходит, когда \nначальная серия заготовок короче, чем серия, за которой следует последняя электронная линия в последовательности строк.
    • #matchприсоединяет ведущее \newlines необходимого для начала :printцикла любых из :indent«s выходных последовательностей , которые приводят спичку в любой командной строке арг. Те последовательности, которые не отображаются пустыми - и полученная пустая строка передается обратно :indent.
mikeserv
источник
2
Ваше сед-фу сильное. Конечно, вы можете сделать то же самое с manperl(){ man $1 | perl -00ne "print if /^\s*$2\b/"; }и затем manperl sh SYNOPSISили manperl sh read:)
Terdon
@terdon - нет, ты не можешь. Это не ест вклад. Я мог бы сделать то же самое, что и в этом случае sed 'H;$!d;g;s/\(\(\n *\)match\([^\n]*\)\2 \)\{1,\}\)*.\{,1\}/\1/g'... возможно, это работает ... но для этого нужно проглотить файл и проанализировать все сразу. Это работает в потоке - он может обрабатывать ввод любого размера, если строки не астрономически длинные. Он печатает , как это работает - и он разбирает все man«s \backslash ускользает для загрузки. Но manэто всего лишь одно приложение - я применил его и к другим проблемам ...
mikeserv
1
Я просто дергаю твою цепь, так как я могу сделать то, что ты описал, с крошечным лайнером. Обратите внимание, однако, что он не поглощает файл целиком, он работает в потоке. Он просто определяет использование строк \n\nвместо, \nно все равно может обрабатывать ввод любого размера и печатать, как это работает. Смотрите «режим абзаца» здесь: perldoc.perl.org/perlrun.html
terdon
@terdon Может быть, это был бы лучший способ пойти сюда. В sedнем можно сделать так: '/./{H;$!d' -e '};x;now work the paragraph...'. Я тоже часто так делаю. Но я изначально написал первую часть для просмотра журнала в течение неограниченного времени, и даже такое поведение было ненадежным - буфер может взорваться при определенных условиях. Это было только вдвое меньше - manстало сложнее. Я посмотрел man -Hпосле получения manописанного выше синопа и думаю, что, возможно, будет проще работать с машинным HTML, который groff может печатать в системах GNU. Я вроде как по локоть уже thoug
mikeserv
@terdon - Я сам догадался и попробовал подход, ориентированный на абзацы, но он проще как есть. Это получает разделы. Лайк mansed cmd DESCRIPTIONполучает раздел ОПИСАНИЕ - и все включенные. Совпадающий поиск печатается целиком, как будто его уровень отступа был верхним. Он даже пропускает ложные срабатывания, игнорируя параграфы, которые соответствуют, но затем не отступают дальше. Он сопоставляет свои аргументы через цветовые экранированные символы и не обрабатывает их до тех пор, пока он точно не будет готов напечатать строку. Все это очень трудно для меня сделать с большим количеством данных, чем одна строка за раз.
mikeserv
1

Каждая оболочка имеет свой набор встроенных функций. Хотя есть общие черты, у каждого из них есть свои особенности, которые необходимо задокументировать.

В таких системах, как Linux и FreeBSD (и OSX, которая наследуется от FreeBSD), где каждая оболочка предоставляется в виде отдельного пакета, нет man-страницы для встроенных функций; вместо этого каждое встроенное задокументировано в справочной странице оболочки. Так что прочитайте справочную страницу bash для документации по killвстроенной функции bash , прочитайте справочную страницу dash для документации по killвстроенной функции dash и т. Д. Есть также справочная страница для killавтономной утилиты.

См. Могу ли я получить отдельные страницы руководства для встроенных команд bash? для manфункции, которая показывает внутреннюю документацию bash вместо страницы руководства, если аргумент является именем встроенной функции.

Существуют варианты Unix, которые предоставляют справочные страницы для встроенных команд оболочки - на самом деле, большинство коммерческих вариантов делают. Это возможно, потому что система поставляется либо с одной оболочкой, либо с набором известных оболочек. На странице руководства обсуждаются различия между оболочками. Например, fg(1)страница на Solaris 10 имеет секции для sh, kshи csh. Страница fg(1)man в AIX 7.1 ссылается на «оболочку Korn» и «оболочку POSIX», но обсуждает их вместе (они поддерживают одинаковые функции fg). Страница fg(1)man на Tru64 5.0 обсуждает встроенную ksh и отсылает пользователей csh к csh(1)странице man. ШОСпо-видимому, поставляется с одной оболочкой. Вы можете установить другие оболочки в качестве дополнительных пакетов в этих операционных системах; Если вы используете собственную оболочку, вы должны помнить, что страницы руководства для встроенных команд не будут релевантными при использовании оболочки не по умолчанию.

Жиль "ТАК - перестань быть злым"
источник