Какова цель команды, которая ничего не делает, будучи чуть ли не лидером комментария, но на самом деле является встроенной оболочкой и сама по себе?
Это медленнее, чем добавление комментария в ваши скрипты примерно на 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
Я предполагаю, что я действительно ищу, какое историческое приложение могло бы иметь.
Исторически, оболочки 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
, хотя это, конечно, детали, специфичные для реализации.
exec
?
true
- это обычная встроенная функция, но он неверен в том, что exec
использует /bin/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
Это делает чистый script. Это невозможно сделать с помощью "#".
Кроме того,
: >afile
является одним из простейших способов гарантировать существование "файла", но имеет длину 0.
>afile
еще проще и достигает того же эффекта.
Полезное приложение для: есть, если вас интересует только расширение параметров для их побочных эффектов, а не передача их результата команде. В этом случае вы используете PE в качестве аргумента для: или false, в зависимости от того, хотите ли вы иметь статус выхода 0 или 1. Пример может быть : "${var:=$1}"
. Поскольку :
является встроенным, он должен быть довольно быстрым.
: $((a += 1))
(операторы ++
и --
не нужно реализовывать в соответствии с POSIX.). В bash, ksh и других возможных оболочках вы также можете сделать: ((a += 1))
или ((a++))
но это не указано в POSIX.
(())
указывается как дополнительная функция. «Если последовательность символов, начинающаяся с« ((», будет обработана оболочкой как арифметическое расширение, если ей предшествует символ« $ », оболочки, в которых реализовано расширение, посредством которого« ((выражение)) »оценивается как арифметическое выражение, можно обработать "((" как введение в качестве арифметической оценки вместо команды группировки. "
:
также может быть для комментариев блоков (аналогично/* */на языке C). Например, если вы хотите пропустить блок кода в script, вы можете сделать это:
: << 'SKIP'
your code block here
SKIP
Если вы хотите усечь файл на нулевые байты, который полезен для очистки журналов, попробуйте следующее:
:> file.log
> file.log
проще и достигает того же эффекта.
Он похож на pass
в Python.
Одним из способов было бы отключить функцию до тех пор, пока она не будет записана:
future_function () { :; }
Вы можете использовать его вместе с backticks (``
) для выполнения команды без отображения ее вывода, например:
: `some_command`
Конечно, вы могли бы просто сделать some_command > /dev/null
, но :
-версия несколько короче.
Говоря, я бы не рекомендовал это делать, поскольку это просто смутило бы людей. Это просто пришло в голову как возможный случай использования.
Еще два использования, не упомянутые в других ответах:
Возьмем этот пример script:
set -x
: Logging message here
example_command
Первая строка, set -x
, заставит оболочку распечатать команду перед ее запуском. Это довольно полезная конструкция. Недостатком является то, что обычный оператор echo Log message
теперь печатает сообщение дважды. Метод двоеточия оборачивается этим. Обратите внимание, что вам все равно придется избегать специальных символов, как и для echo
.
Я видел, как он использовался в cron-заданиях, например:
45 10 * * * : Backup for database ; /opt/backup.sh
Это задание cron, которое запускает script /opt/backup.sh
каждый день в 10:45. Преимущество этого метода заключается в том, что он делает для более привлекательных пользователей электронной почты, когда /opt/backup.sh
печатает какой-то результат.
set -x
распечатанные команды (включая что-то вроде : foobar
) переходят в stderr.
Это также полезно для программ 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(){ ... }
.)
: //;
и ~function(){}
делать? Спасибо :)
~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 ($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
. Это упражнение, оставленное читателю.$1
. В то же время он будет загромождать пространство имен, если вы отправляете библиотеку. Если вам это не нравится, вы можете либо изменить имя на что-то вроде lib_help
, либо на самом деле проверить args для --help
в главном коде и вызвать функцию справки вручную.Я видел это использование в script и думал, что это хорошая замена для вызова базового имени в script.
oldIFS=$IFS
IFS=/
for basetool in $0 ; do : ; done
IFS=$oldIFS
...
это замена кода: basetool=$(basename $0)