Какова цель: (двоеточие) GNU Bash встроенная?

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

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

# poor man 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

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

Ответ 1

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

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

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

Так как if требует непустого предложения then, а комментарии не считаются непустыми, : служит как не-op.

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

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

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

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

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

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

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

Ответ 2

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

#!/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

Это делает чистый script. Это невозможно сделать с помощью "#".

Кроме того,

: >afile

является одним из простейших способов гарантировать существование "файла", но имеет длину 0.

Ответ 3

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

Ответ 4

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

: << 'SKIP'

your code block here

SKIP

Ответ 5

Он похож на pass в Python.

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

future_function () { :; }

Ответ 6

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

:> file.log

Ответ 7

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

: `some_command`

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

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

Ответ 8

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

Вход

Возьмем этот пример script:

set -x
: Logging message here
example_command

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

Названия вакансий Cron

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

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

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

Ответ 9

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

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

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

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


Некоторая экспликация, по запросу:

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

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

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

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

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

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

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

Ответ 10

Функции самодокументирования

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

Предположим, у вас есть библиотека script 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 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
        "[email protected]"
    else
        --help
    fi
fi

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

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

Ответ 11

Я видел это использование в script и думал, что это хорошая замена для вызова базового имени в script.

oldIFS=$IFS  
IFS=/  
for basetool in $0 ; do : ; done  
IFS=$oldIFS  

... это замена кода: basetool=$(basename $0)