Каков вариант использования noop [:] в bash?

124

Я искал noop в bash (:), но не смог найти никакой хорошей информации. Какова точная цель или вариант использования этого оператора?

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

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

Сообщите мне обо всех случаях использования этого оператора в режиме реального времени или в любом месте, где его использование обязательно.

Мандар Панде
источник
Взгляните на stackoverflow.com/questions/7444504/…
Стефан Рубероль
Обратите внимание, что :встроенный существует в оболочке bourne и ksh, а также в bash.
ghoti
См. Также :в tldp.org/LDP/abs/html/special-chars.html
choroba
6
Как это не настоящий вопрос? Я думаю, это действительно хороший вопрос. У меня даже есть хорошее применение, но я не могу опубликовать ответ.
Steven Lu

Ответы:

176

Это больше по историческим причинам. Встроенное двоеточие :в точности эквивалентно true. Традиционно используется, trueкогда важно возвращаемое значение, например, в бесконечном цикле:

while true; do
  echo 'Going on forever'
done

Традиционно используется, :когда синтаксис оболочки требует команды, но вам нечего делать.

while keep_waiting; do
  : # busy-wait
done

В :даты BUILTIN все пути назад к Thompson оболочки , это присутствует в Unix v6 . :был индикатором ярлыка для gotoзаявления оболочки Томпсона . Метка может быть любым текстом, поэтому используется :как индикатор комментария (если его нет goto comment, то : commentэто фактически комментарий). Bourne оболочка не была , gotoно продолжала :.

Обычная идиома , которая использует :это : ${var=VALUE}, который устанавливает , varчтобы , VALUEесли это не установлено , и не делает ничего , если varуже было установлено. Эта конструкция существует только в форме подстановки переменных, и эта подстановка переменных должна каким-то образом быть частью команды: команда no-op служит хорошо.

См. Также Какую цель выполняет встроенная толстая кишка? ,

Жиль 'ТАК - перестань быть злым'
источник
11
Хорошее резюме. Кроме того, исходная оболочка Bourne не использовалась #для комментариев (или #!/bin/shкомментариев); :команда представила комментарии, и горе наивного программиста , который сделал хорошую коробку звезд в комментариях: : ****(или, что еще хуже, : * * *).
Джонатан Леффлер,
3
@JonathanLeffler: Позор мне, но я этого не понимаю. Что бы случилось : * * *?
DevSolar 05
7
Поскольку :это команда, оболочка все равно должна обработать свои аргументы, прежде чем сможет обнаружить, что :их игнорирует. Чаще всего вы просто заставляете оболочку выполнять дополнительную работу по расширению *списка файлов в текущем каталоге; это фактически не повлияет на работу скрипта.
chepner 05
Я полагаю, это может привести к сбою вашего скрипта, если вы случайно запустите каталог с большим количеством файлов, что приведет к превышению лимита длины команды расширением glob? Может быть, только если у вас есть set -e. В любом случае, надеюсь, мы все сможем просто согласиться на использование #:)
Джек О'Коннор,
2
Иногда я использую, while : ; do ... ; doneесли мне нужен быстрый и грязный бесконечный цикл. (Обычно sleepв цикле есть символ, и я нажимаю Ctrl-C, чтобы его убить.)
Кейт Томпсон
21

Я использую его для операторов if, когда комментирую весь код. Например, у вас есть тест:

if [ "$foo" != "1" ]
then
    echo Success
fi

но вы хотите временно закомментировать все содержимое:

if [ "$foo" != "1" ]
then
    #echo Success
fi

Это заставляет bash выдавать синтаксическую ошибку:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

В Bash не может быть пустых блоков (WTF). Итак, вы добавляете блокировку:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

или вы можете использовать no-op, чтобы закомментировать строки:

if [ "$foo" != "1" ]
then
    : echo Success
fi
Стивен Остермиллер
источник
12

Если вы используете set- ethen || :, это отличный способ не выходить из скрипта в случае сбоя (он явно заставляет его пройти).

Крис Пфол
источник
1
Я нашел это полезным для некоторых приложений, вызывающих сценарии оболочки. Например, Automator на Mac завершит работу в случае ошибки и не скажет почему. Но использование || :последующей обработки ошибки самостоятельно с помощью an exit 0позволит вам отображать все stdout и stderr в окне приложения во время отладки. Подумайте, { foo ... 2>&1; } || :что намного проще, чем установка более сложных ловушек и условностей.
Beejor
7

Вы могли бы использовать :команду, которая работает успешно, но ничего не делает. В этом примере команда «многословность» по умолчанию отключена, для нее установлено значение :. Параметр «v» включает его.

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  
отметка
источник
строго говоря, присвоение значения переменной «что-то делает», поэтому оно не вызывает ошибки в вашем операторе case.
qodeninja
@qodeninja: Никто не утверждал, что присвоение переменной «ничего не дало»; Дело в том, что $(verbosity $i)последнее (и условно) ничего не делает.
Гонки
6

Игнорировать aliasаргументы

Иногда вам нужен псевдоним, который не принимает никаких аргументов. Вы можете сделать это, используя ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there
Ulysse BN
источник
не тот, кто проголосовал против, но этот ответ :, к сожалению, не демонстрирует, как работает. Приведенный вами пример кажется излишним.
qodeninja
2
Вопрос не в том, как это работает, а в том, как это использовать . Это правда, что мой ответ немного похож на stackoverflow.com/a/37170755/6320039 . Но на самом деле это не тот же вариант использования. Я был бы рад улучшить свой ответ, но я действительно не знаю, как здесь ...
Ulysse BN
1
Это немного забавно - угловой случай, если хотите, - но все же довольно интересно и может быть умным трюком, чтобы иметь его в заднем кармане.
Mike S
2
То же самое может быть достигнуто с#
Zoey Hewll
2
@ZoeyHewll, не совсем так. Поскольку псевдонимы заменяются текстуально перед выполнением любого вида, использование псевдонима to #заставит игнорировать все, что синтаксически следует после в той же строке. Это действительно другое (рассмотрите my_alias arg ; other_commandили, наоборот my_alias arg1 {BACKSLASH}{NEWLINE} arg2), и, вероятно, это не то, что вы хотите, поскольку это может вызвать синтаксические ошибки (угадайте, что while my_alias ; do true ; doneили while true ; do my_alias ; doneбудет делать).
Maëlan
4

Один из вариантов использования - как многострочные комментарии, или чтобы закомментировать часть вашего кода в целях тестирования, используя его вместе с файлом здесь.

: << 'EOF'

This part of the script is a commented out

EOF

Не забывайте использовать кавычки вокруг EOF чтобы любой код внутри не оценивался, например $(foo). Она также может быть стоит использовать интуитивное имя терминатора , как NOTES, SCRATCHPADили TODO.

Джеффри Ричи
источник
Классный трюк! Специально для заметок и кода, требующего десятков символов "#" в жесткой упаковке. Побочный эффект заключается в том, что в отличие от строк комментариев некоторые средства выделения синтаксиса будут игнорировать heredocs, раскрашивая их как обычный код (или, по крайней мере, простой текст). Это может быть отличным вариантом для изучения закомментированного кода или сохранения ваших глаз от абзацев в неоновом цвете комментариев. Единственное предостережение: такие блоки следует отмечать как комментарии в общем коде, поскольку синтаксис уникален и может вызвать путаницу. С другой стороны, мы говорим об этой оболочке, в которой вероятность путаницы носит системный характер.
Beejor
3

Два моих.

Встраивать комментарии POD

Довольно забавное приложение :для встраивания комментариев POD в сценарии bash. , чтобы можно было быстро создавать страницы . Конечно, со временем можно было бы переписать весь сценарий на Perl ;-)

Связывание функций времени выполнения

Это своего рода шаблон кода для привязки функций во время выполнения. Fi, имейте функцию отладки, чтобы что-то делать, только если установлен определенный флаг:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

Ты получаешь:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar
sphakka
источник
2

Иногда предложения no-op могут сделать ваш код более читабельным.

Это может быть вопрос мнения, но вот пример. Предположим, вы создали функцию, которая работает по двум путям unix. Он вычисляет «путь изменения», необходимый для перехода от одного пути к другому. Вы накладываете ограничение на свою функцию, что оба пути должны начинаться с '/' ИЛИ ​​оба не должны.

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

Некоторые разработчики захотят удалить no-op, но это будет означать отрицание условия:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

Теперь, на мой взгляд, из предложения if не так ясно, при каких условиях вы хотели бы пропустить выполнение функции. Чтобы исключить бездействие и сделать это четко, вам нужно вынести предложение if из функции:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

Это выглядит лучше, но во многих случаях мы не можем этого сделать; мы хотим, чтобы проверка выполнялась внутри функции.

Так как часто это происходит? Не слишком часто. Может, раз или два в год. Это случается достаточно часто, чтобы вы знали об этом. Я не уклоняюсь от его использования, когда считаю, что он улучшает читаемость моего кода (независимо от языка).

Bitdiot
источник
3
Если вы отвечаете на вопрос о цели :, вы должны использовать :, а не trueв ответе. Тем не менее, самый простой способ свести на нет условных здесь использовать одну [[ ... ]]команду и префикс с !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner 05
1
Ах, очень мило, и я должен проголосовать против своего ответа. Хммм .... Я все еще думаю о примерах, где мне приходилось использовать предложение о запрете операций. Это лучшее, что я придумал.
Bitdiot 05
На самом деле он просто сохранен для обратной совместимости, хотя : ${...=...}, возможно, выглядит менее неудобно, чем true ${...=...}. Некоторые могут предпочесть , while :; doчтобы while true; doза краткость , а также.
chepner 05
2

В некоторой степени связанный с этим ответом , я считаю, что этот запрет на выполнение операций довольно удобен для взлома сценариев полиглотов . Например, вот правильный комментарий как для bash, так и для vimscript:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

Конечно, мы могли использовать trueточно так же, но :знак препинания, а не нерелевантное английское слово, дает понять, что это синтаксический токен.


Что касается того, зачем кому-то делать такую ​​сложную вещь, как написание сценария-полиглота (помимо того, что это круто): это оказывается полезным в ситуациях, когда мы обычно пишем несколько файлов сценария на нескольких разных языках, причем файл Xотносится к файлу Y.

В такой ситуации объединение обоих скриптов в один файл-полиглот позволяет избежать любой работы Xпо определению пути к Y(это просто "$0"). Что еще более важно, это делает более удобным перемещать или распространять программу.

  • Типичный пример. Существует хорошо известная, давняя проблема с shebangs: большинство систем (включая Linux и Cygwin) позволяют передавать интерпретатору только один аргумент. Следующий шебанг:

    #!/usr/bin/env interpreter --load-libA --load-libB

    запустит следующую команду:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"

    а не по назначению:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Таким образом, вы в конечном итоге напишете сценарий-оболочку, например:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Вот где на сцену выходит полиглоссия.

  • Более конкретный пример. Однажды я написал сценарий bash, который, помимо прочего, вызывал Vim. Мне нужно было дать Vim дополнительную настройку, которую можно было бы сделать с опцией --cmd "arbitrary vimscript command here". Однако эта настройка была существенной, поэтому встраивание ее в строку было бы ужасным (если вообще возможно). Следовательно, лучшим решением было записать его полностью в некоторый файл конфигурации, а затем заставить Vim читать этот файл с помощью -S "/path/to/file". Таким образом, я получил файл bash / vimscript для полиглотов.

Maëlan
источник
1

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


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}
Брендан Абель
источник
0

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

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

но теперь вы хотите выполнить команды условно, и вы хотите показать команды, которые будут выполнены (пробный запуск):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

поэтому, если вы установите DEBUG и NOEXEC, вторая команда никогда не появится. это потому, что первая команда никогда не выполняется (потому что NOEXEC не пуст), но оценка этого факта оставляет вам возвращение 1, что означает, что подчиненная команда никогда не выполняется (но вы этого хотите, потому что это пробный запуск). поэтому, чтобы исправить это, вы можете сбросить значение выхода, оставшееся в стеке, с помощью noop:

[ -z "$NOEXEC" ] && $cmd || :
ekkis
источник