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

225

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

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

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

  • 5
    Тот же вопрос по Unix и Linux : Какую цель выполняет встроенное двоеточие?
  • 49
    @Caleb - я спросил об этом за два года до этого.
Показать ещё 1 комментарий
Теги:
built-in

11 ответов

262
Лучший ответ

Исторически, оболочки 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, хотя это, конечно, детали, специфичные для реализации.

  • 0
    Вы имели в виду, что обычные встроенные модули не должны быть совместимы с exec ?
  • 1
    @OldPro: Нет, он прав в том, что true - это обычная встроенная функция, но он неверен в том, что exec использует /bin/true вместо встроенной.
Показать ещё 4 комментария
49

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

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

  • 17
    >afile еще проще и достигает того же эффекта.
  • 0
    @earl прав; Я отредактировал свой ответ соответственно.
Показать ещё 2 комментария
40

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

  • 1
    Вы также можете использовать его для побочных эффектов арифметического расширения:: : $((a += 1)) (операторы ++ и -- не нужно реализовывать в соответствии с POSIX.). В bash, ksh и других возможных оболочках вы также можете сделать: ((a += 1)) или ((a++)) но это не указано в POSIX.
  • 0
    @pabouk Да, это все правда, хотя (()) указывается как дополнительная функция. «Если последовательность символов, начинающаяся с« ((», будет обработана оболочкой как арифметическое расширение, если ей предшествует символ« $ », оболочки, в которых реализовано расширение, посредством которого« ((выражение)) »оценивается как арифметическое выражение, можно обработать "((" как введение в качестве арифметической оценки вместо команды группировки. "
30

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

: << 'SKIP'

your code block here

SKIP
  • 2
    Плохая идея. Любые подстановки команд внутри этого документа все еще обрабатываются.
  • 22
    Не такая плохая идея. Вы можете избежать изменения / замены переменных в этом документе, заключив в кавычки разделитель:: << 'SKIP'
23

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

:> file.log
  • 15
    > file.log проще и достигает того же эффекта.
  • 45
    Да, но счастливое лицо это то, что он делает для меня:>
Показать ещё 2 комментария
23

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

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

future_function () { :; }
20

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

: `some_command`

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

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

  • 22
    Это небезопасно, если команда собирается сбросить несколько мегабайт выходных данных, поскольку оболочка буферизирует выходные данные и затем передает их в качестве аргументов командной строки (пространство в стеке) в ':'.
19

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

Вход

Возьмем этот пример 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 печатает какой-то результат.

  • 0
    Где находится журнал по умолчанию? Могу ли я установить местоположение журнала? Является ли целью больше создание выходных данных в stdout во время сценариев / фоновых процессов?
  • 1
    @domdambrogia При использовании set -x распечатанные команды (включая что-то вроде : foobar ) переходят в stderr.
8

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

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

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

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


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

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

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

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

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

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

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

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

  • 0
    Здравствуйте, вы можете объяснить, что : //; и ~function(){} делать? Спасибо :)
  • 1
    @Stphane Добавил разбивку! Что касается ~function(){} , это немного сложнее. Здесь есть пара других ответов, которые касаются этого, хотя ни один из них не объясняет это к моему удовлетворению… если ни один из этих вопросов не объясняет это достаточно хорошо для вас, не стесняйтесь публиковать его здесь как вопрос, я буду с удовольствием подробно расскажу о новом вопросе.
Показать ещё 2 комментария
5

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

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

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню