Какова цель встроенной GNU Bash (двоеточие)? - PullRequest
301 голосов
/ 12 июля 2010

Какова цель команды, которая ничего не делает, будучи всего лишь лидером комментариев, но на самом деле является встроенной оболочкой сама по себе?

Это медленнее, чем вставка комментария в ваши сценарии примерно40% за звонок, что, вероятно, сильно варьируется в зависимости от размера комментария.Единственные возможные причины, которые я вижу для этого, следующие:

# poor man's delay function
for ((x=0;x<100000;++x)) ; do : ; done

# inserting comments into string of commands
command ; command ; : we need a comment in here for some reason ; command

# an alias for `true' (lazy programming)
while : ; do command ; done

Я думаю, что я действительно ищу, так это то, какое историческое применение оно могло иметь.

Ответы [ 11 ]

371 голосов
/ 12 июля 2010

Исторически , оболочки Борна не имели true и false в качестве встроенных команд.true вместо этого просто связывался с :, а false с чем-то вроде let 0.

: немного лучше, чем true для переносимости к древним оболочкам, полученным из Борна.В качестве простого примера рассмотрим отсутствие оператора конвейера ! и оператора списка || (как это было в случае некоторых древних оболочек Борна).При этом предложение else оператора if остается единственным средством ветвления на основе состояния выхода:

if command; then :; else ...; fi

Поскольку для if требуется непустое предложение then и комментарии don 'не считая непустых, : служит неактивным.

В настоящее время (то есть: в современном контексте) вы обычно можете использовать либо : или true.Оба указываются в POSIX, а некоторые находят true более легким для чтения.Однако есть одно интересное отличие: : - это так называемая POSIX специальная встроенная , тогда как true - обычная встроенная .

  • Специальные встроенные элементы должны быть встроены в оболочку;Обычные встроенные функции только «типично» встроены, но это не гарантируется строго.Обычно не должно быть обычной программы с именем : с функцией true в PATH большинства систем.

  • Вероятно, наиболее важным отличием является то, что со специальными встроенными модулямилюбая переменная, установленная встроенной программой - даже в среде во время простой оценки команды - сохраняется после завершения команды, как показано здесь с использованием ksh93:

    $ unset x; ( x=hi :; echo "$x" )
    hi
    $ ( x=hi true; echo "$x" )
    
    $
    

    Обратите внимание, что Zsh игнорирует это требование, как и GNUBash, за исключением случаев, когда он работает в режиме совместимости с POSIX, но все другие основные оболочки, производные от POSIX sh, наблюдают это, включая dash, ksh93 и mksh.

  • Другое отличие состоит в том, что обычные встроенные модули должныбыть совместимым с exec - продемонстрировано здесь с использованием Bash:

    $ ( exec : )
    -bash: exec: :: not found
    $ ( exec true )
    $
    
  • POSIX также явно отмечает, что : может быть быстрее, чем true, хотя это, конечно, реализация-специфичная деталь.

57 голосов
/ 12 июля 2010

Я использую его для простого включения / выключения переменных команд:

#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
    vecho=":"     # no "verbose echo"
else
    vecho=echo    # enable "verbose echo"
fi

$vecho "Verbose echo is ON"

Таким образом

$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON

Это делает для чистого сценария.Это нельзя сделать с помощью «#».

Кроме того,

: >afile

- это один из самых простых способов гарантировать, что «afile» существует, но имеет длину 0.

50 голосов
/ 04 февраля 2011

Полезное приложение для: если вы заинтересованы только в использовании расширений параметров для их побочных эффектов, а не в том, чтобы фактически передавать их результат команде. В этом случае вы используете PE в качестве аргумента либо:, либо false, в зависимости от того, хотите ли вы выйти из состояния 0 или 1. Примером может быть : "${var:=$1}". Поскольку : является встроенным, оно должно быть довольно быстрым.

39 голосов
/ 21 сентября 2013

: также может быть для блочного комментария (аналогично / * * / на языке Си). Например, если вы хотите пропустить блок кода в вашем скрипте, вы можете сделать это:

: << 'SKIP'

your code block here

SKIP
30 голосов
/ 23 июня 2011

Если вы хотите обрезать файл до нуля байтов, что полезно для очистки журналов, попробуйте следующее:

:> file.log
27 голосов
/ 12 июля 2010

Это похоже на pass в Python.

Одно из применений - отключить функцию, пока она не будет написана:

future_function () { :; }
25 голосов
/ 24 апреля 2015

Еще два использования, не упомянутые в других ответах:

Вход

Взять этот пример скрипта:

set -x
: Logging message here
example_command

В первой строке, set -x, оболочка выводит команду перед ее выполнением. Это довольно полезная конструкция. Недостатком является то, что обычный оператор типа echo Log message теперь печатает сообщение дважды. Метод двоеточия обходит это. Обратите внимание, что вам все равно придется экранировать специальные символы так же, как и для echo.

Cron должности

Я видел, как он используется в заданиях cron, например:

45 10 * * * : Backup for database ; /opt/backup.sh

Это задание cron, которое запускает скрипт /opt/backup.sh каждый день в 10:45. Преимущество этого метода заключается в том, что он улучшает внешний вид тем электронной почты, когда /opt/backup.sh выводит какой-либо вывод.

21 голосов
/ 12 июля 2010

Вы можете использовать его вместе с обратными галочками (``) для выполнения команды без отображения ее вывода, например:

: `some_command`

Конечно, вы могли бы просто сделать some_command > /dev/null, но : -версия несколько короче.

Как говорится, я бы не советовал делать это на самом деле, потому что это просто смущало бы людей. Это просто пришло на ум в качестве возможного варианта использования.

13 голосов
/ 30 декабря 2015

Это также полезно для программ полиглота:

#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }

Теперь это исполняемый сценарий оболочки и программа JavaScript: это означает, что ./filename.js, sh filename.js и node filename.js все работают.

(определенно немного странно, но, тем не менее, эффективно.)


Некоторые объяснения, как и требовалось:

  • Shell-скрипты оцениваются построчно; и команда exec при запуске завершает работу оболочки, а заменяет ее процесс на результирующую команду. Это означает, что для оболочки программа выглядит так:

    #!/usr/bin/env sh
    ':' //; exec "$(command -v node)" "$0" "$@"
    
  • Пока в слове не происходит расширения или псевдонима параметра, любое слово в сценарии оболочки может быть заключено в кавычки без изменения его значения; это означает, что ':' эквивалентно : (здесь мы заключили его в кавычки только для достижения описанной ниже семантики JavaScript)

  • ... и, как описано выше, первая команда в первой строке не используется (переводится как : // или, если вы предпочитаете заключать слова в кавычки, ':' '//'. Обратите внимание, что // здесь не имеет особого значения, как в JavaScript, это просто бессмысленное слово, которое выбрасывается.)

  • Наконец, вторая команда в первой строке (после точки с запятой) - это настоящее ядро ​​программы: это вызов exec, который заменяет вызываемый shell-скрипт , с процессом Node.js, вызванным для оценки остальных скрипта.

  • Между тем, первая строка в JavaScript анализируется как строковый литерал (':'), а затем комментарий, который удаляется; Таким образом, для JavaScript программа выглядит так:

    ':'
    ~function(){ ... }
    

    Поскольку строковый литерал находится в строке сам по себе, он является оператором no-op и поэтому удаляется из программы; это означает, что вся строка удаляется, оставляя только ваш программный код (в данном примере тело function(){ ... }.)

11 голосов
/ 28 марта 2016

Самодокументируемые функции

Вы также можете использовать : для встраивания документации в функцию.

Предположим, у вас есть библиотечный скрипт mylib.sh, предоставляющий множество функций. Вы можете либо получить библиотеку (. mylib.sh) и сразу после этого вызывать функции (lib_function1 arg1 arg2), либо избежать загромождения своего пространства имен и вызвать библиотеку с аргументом функции (mylib.sh lib_function1 arg1 arg2).

Не было бы неплохо, если бы вы могли также набрать mylib.sh --help и получить список доступных функций и их использование, без необходимости вручную поддерживать список функций в тексте справки?

#!/bin/bash

# all "public" functions must start with this prefix
LIB_PREFIX='lib_'

# "public" library functions
lib_function1() {
    : This function does something complicated with two arguments.
    :
    : Parameters:
    : '   arg1 - first argument ($1)'
    : '   arg2 - second argument'
    :
    : Result:
    : "   it's complicated"

    # actual function code starts here
}

lib_function2() {
    : Function documentation

    # function code here
}

# help function
--help() {
    echo MyLib v0.0.1
    echo
    echo Usage: mylib.sh [function_name [args]]
    echo
    echo Available functions:
    declare -f | sed -n -e '/^'$LIB_PREFIX'/,/^}$/{/\(^'$LIB_PREFIX'\)\|\(^[ \t]*:\)/{
        s/^\('$LIB_PREFIX'.*\) ()/\n=== \1 ===/;s/^[ \t]*: \?['\''"]\?/    /;s/['\''"]\?;\?$//;p}}'
}

# main code
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
    # the script was executed instead of sourced
    # invoke requested function or display help
    if [ "$(type -t - "$1" 2>/dev/null)" = function ]; then
        "$@"
    else
        --help
    fi
fi

Несколько комментариев о коде:

  1. Все «публичные» функции имеют одинаковый префикс. Только они предназначены для вызова пользователем и перечислены в тексте справки.
  2. Функция самодокументирования опирается на предыдущий пункт и использует declare -f для перечисления всех доступных функций, а затем фильтрует их через sed для отображения только функций с соответствующим префиксом.
  3. Рекомендуется заключать документацию в одинарные кавычки, чтобы предотвратить нежелательное расширение и удаление пробелов. Вы также должны быть осторожны при использовании апострофов / цитат в тексте.
  4. Вы можете написать код для интернализации префикса библиотеки, т. Е. Пользователю нужно только набрать mylib.sh function1, и он будет внутренне переведен в lib_function1. Это упражнение оставлено читателю.
  5. Справочная функция называется "--help". Это удобный (то есть ленивый) подход, который использует механизм вызова библиотеки для отображения самой справки, без необходимости кодировать дополнительную проверку для $1. В то же время, он будет загромождать ваше пространство имен, если вы создадите библиотеку. Если вам это не нравится, вы можете либо изменить имя на что-то вроде lib_help, либо на самом деле проверить аргументы для --help в основном коде и вызвать функцию справки вручную.
...