Какова цель: (двоеточие) GNU Bash builtin?


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

это медленнее, чем вставка комментария в ваши скрипты примерно на 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 255

11 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

: << 'SKIP'

your code block here

SKIP

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

одно использование было бы заглушить функцию, пока она не будет записана:

future_function () { :; }

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

:> file.log

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

: `some_command`

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

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

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

лесозаготовки

взять этот скрипт:

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 выводит некоторые выходные данные.

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

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

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

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


некоторые пояснения, как и просили:

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

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

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

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

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

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

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

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

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

Предположим, у вас есть библиотека 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 ()'
    : '   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===  ===/;s/^[ \t]*: \?['\''"]\?/    /;s/['\''"]\?;\?$//;p}}'
}

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

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

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

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

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

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