В чем заключается полезность команды: в сценариях оболочки, если она явно ничего не делает?

27

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

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

DQdlM
источник
2
Смотрите также на этот вопрос , который имеет еще лучший ответ здесь , а именно , что :требуется , чтобы быть встроенным, а trueне так , что влияет на объем переменных
Старый Pro
2
Это явно ничего не делает изящно .
mikeserv

Ответы:

19

Я обычно использую trueв петлях; Я думаю, что это более понятно:

while true; do
    ...
done

Единственное место, которое я нашел, что :действительно удобно, это операторы case, если вам нужно что-то сопоставить, но вы не хотите ничего делать. Например:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac
Сумасшедший ученый
источник
3
Я согласен. trueдля условия, :для NOP.
jw013
1
Баш принимает case a in a ) ;; esac. Есть ли снаряды, которые этого не принимают?
Каз
@Kaz: Согласно грамматике оболочки POSIX , case ${var} in value);; *) do_something;; esacэто приемлемо. Команда :не нужна для пустых случаев.
Ричард Хансен
12

Первоначально он использовался для определения того, что это была программа оболочки Bourne, а не скомпилированная программа C. Это было до shebang и нескольких языков сценариев (csh, perl). Вы все еще можете запустить скрипт, начинающийся с ::

$ echo : > /tmp/xyzzy
$ chmod +x /tmp/xyzzy
$ ./xyzzy

Как правило, он запускает скрипт против $SHELL(или /bin/sh).

С тех пор основным использованием является оценка аргументов. Я все еще использую:

: ${EDITOR:=vim}

установить значение по умолчанию в скрипте.

Arcege
источник
11

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

while :
do
    ...stuff...
done

Это будет продолжаться вечно, пока не будет вызван breakлибо exitвызов, либо оболочка получит сигнал завершения.

Кайл Джонс
источник
3
Я чувствую, что while true; do ...; doneсообщает о своих намерениях читателю лучше, чемwhile :; do ...; done
Ричард Хансен
9

Когда вы хотите использовать выражение "never" в сценариях оболочки, вы либо используете условие "not", которое может показаться глупым для некоторых тестов, либо вы используете ':' в предложении true, с реальным кодом в false- пункт.

if [ some-exotic-condition ]
then
    :
else
    # Real code here
fi

«Экзотическое состояние» может быть чем-то, что вы не хотите отрицать, или это будет намного понятнее, если вы не используете «негативную логику».

Брюс Эдигер
источник
1
Это также проявляется в сценариях, сгенерированных autoconfпотому, что гораздо проще добавить значение :по умолчанию для пустых ветвей, чем выяснить, как отменить условие.
Дитрих Эпп
2
Я не вижу, как торчать !впереди [ some-exotic-condition ]глупо, но излишне : elseпосле этого не глупо.
Каз
У @Kaz есть хорошая точка зрения. Давайте помнить, что придумать экзотические условия в лучшем случае сложно. Если вам нужно отрицать все это, это одно, но это может просто сделать условие менее ясным. Делает '!' свести на нет все условия, или только первый член? Лучше всего иногда иметь выражение «:».
Брюс Эдигер
!Маркер сводит на нет всю команду элемент трубы. while ! grep ... ; do ... done или if ! [ ... ] ; then ... fi. Это в основном внешний по отношению к test/[]синтаксису. См .: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Каз
5

Я использовал это только в дополнение к символу # для временного комментирования строки, в ситуации, когда комментирование строки приводит к синтаксической ошибке из-за дефекта в грамматике оболочки, из-за которого не допускается пустая последовательность команд :

if condition ; then
    :# temporarily commented out command
fi

Без: у нас отсутствует последовательность команд, что является синтаксической ошибкой.

Kaz
источник
4

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

Переменные по умолчанию

#!/bin/sh

# set VAR to "default value" if not already set in the environment
: "${VAR=default value}"

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR}"

Это удобный способ разрешить пользователям вашего сценария оболочки изменять настройки без редактирования сценария. (Тем не менее, аргументы командной строки лучше, потому что вы не рискуете непредвиденного поведения, если у пользователя случайно есть переменная, которую вы используете в своей экспортированной среде.) Вот как пользователь переопределит параметр:

VAR="other value" ./script

${VAR=value}Синтаксис говорит набор VARдля valueесли VARеще не установлен, а затем расширить до значения переменной. Поскольку нам пока не важно значение переменной, она передается в качестве аргумента команде no-op, :чтобы выбросить ее.

Несмотря на то, :что это команда no-op, расширение выполняется оболочкой (а не :командой!) Перед выполнением :команды, поэтому назначение переменной все еще происходит (если применимо).

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

Следующий скрипт также будет работать:

#!/bin/sh

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR=default value}"

Но вышесказанное гораздо сложнее поддерживать. Если ${VAR}над этой printfстрокой добавлена строка с использованием, расширение по умолчанию должно быть перемещено. Если разработчик забывает перенести это назначение, появляется ошибка.

Что-то положить в пустой условный блок

Пустых условных блоков обычно следует избегать, но иногда они полезны:

if some_condition; then
    # todo:  implement this block of code; for now do nothing.
    # the colon below is a no-op to prevent syntax errors
    :
fi

Некоторые люди утверждают, что наличие пустого истинного ifблока может облегчить чтение кода, чем отрицание теста. Например:

if [ -f foo ] && bar || baz; then
    :
else
    do_something_here
fi

легче читать, чем:

if ! [ -f foo ] || ! bar && ! baz; then
    do_something_here
fi

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

  1. Поместите условие в функцию:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
  2. Поместите условие в фигурные скобки (или круглые скобки, но скобки порождают процесс подоболочки, и любые изменения, внесенные в среду внутри подоболочки, не будут видны за пределами подоболочки) перед отрицанием:

    if ! { [ -f foo ] && bar || baz; } then
        do_something_here
    fi
  3. Используйте ||вместо if:

    [ -f foo ] && bar || baz || {
        do_something_here
    }

    Я предпочитаю такой подход, когда реакция простая однострочная, такая как утверждение условий:

    log() { printf '%s\n' "$*"; }
    error() { log "ERROR: $*" >&2; }
    fatal() { error "$@"; exit 1; }
    
    [ -f foo ] && bar || baz || fatal "condition not met"
Ричард Хансен
источник
1

В старой оболочке pre-bourne в древних версиях UNIX :команда изначально предназначалась для указания меток для goto(это была отдельная команда, которая сворачивает ввод туда, где находится метка, поэтому метки не могли быть отдельным синтаксисом, который shell знает о. ifбыла также отдельной командой.) Вскоре она стала использоваться для комментариев, до того, как появился синтаксис комментариев ( #использовался для возврата), и в наши дни для обеспечения совместимости практически ничего.

Random832
источник
0

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

Мартин Стейн
источник
Это не будет точно комментарий, так как : echo write this line > myfileвсе равно создаст пустой файл.
Arcege
5
Как объяснено в ссылке в вопросе, :является НЕ адекватный механизм комментирования.
jw013