Подтвердить что ты не робот

Какой смысл! перед командой в оболочке?

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

В foo.sh:

#!/usr/bin/env bash
set -e
! docker stop foo
! docker rm -f foo
# ... other stuff

Я знаю, что без пробела восклицательный знак используется для замены истории и ! <expression> в соответствии с man page для оценки "Истина, если выражение is false". Но в контексте примера это не имеет смысла для меня.

4b9b3361

Ответ 1

TL; DR: это просто обход флага set -e в конкретной строке, где вы его используете.


Добавление в hek2mgl правильного и полезного ответа.

У вас есть:

set -e
! command

Bash Справочное руководство → Трубопроводы описывает:

Каждая команда в конвейере выполняется в своей собственной подоболочке. Состояние выхода конвейера - это статус выхода последней команды в конвейере (...). Если зарезервированное слово '! предшествует конвейеру, статус выхода является логическим отрицанием статуса выхода, как описано выше. Оболочка ожидает завершения всех команд в конвейере перед возвратом значения.

Это означает, что !, предшествующий команде, отрицает ее статус выхода:

$ echo 23
23
$ echo $?
0
                # But
$ ! echo 23
23
$ echo $?
1

Или:

$ echo 23 && echo "true" || echo "fail"
23
true
$ ! echo 23 && echo "true" || echo "fail"
23
fail

Статус выхода полезен во многих отношениях. В script, используемом вместе с set -e, вызывается script, когда команда возвращает ненулевой статус.

Таким образом, если у вас есть:

set -e
command1
command2

Если command1 возвращает ненулевой статус, script закончит и не перейдет к command2.

Однако есть также интересный момент, о котором говорится в 4.3.1 Set Builtin:

-e

Выйти немедленно, если конвейер (см. "Трубопроводы" ), который может состоять из одной простой команды (см. "Простые команды" ), списка (см. Списки) или составной команды (см. составные команды), возвращает ненулевой статус. Оболочка не выходит из, если неудавшаяся команда является частью списка команд сразу после некоторого времени или до тех пор, пока ключевое слово, часть теста в выражении if, не будет частью любой команды, выполненной в && & &; или || список, за исключением команды, следующей за окончательным && или ||, любая команда в конвейере, но последняя, ​​или , если статус возврата команд перевернут с помощью!. Если составная команда, отличная от подоболочки, возвращает ненулевой статус, потому что команда была неудачной, а -e игнорировалась, оболочка не выходила. Ловушка в ERR, если установлена, выполняется до выхода оболочки.


Принимая во внимание все это, когда у вас есть:

set -e
! command1
command2

Что вы делаете, это обходить флаг set -e в command1. Почему?

  • Если command1 работает правильно, он вернет нулевой статус. ! будет отрицать его, но set -e не будет вызывать выход из-за того, что он возвращается из состояния возврата, инвертированного с помощью!, как описано выше.
  • Если command1 не работает, он вернет ненулевой статус. ! будет отрицать это, поэтому строка вернет нулевой статус, а script продолжит нормально.

Ответ 2

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

set -e
docker stop foo || true

Логическое или истинное делает конвейер всегда 0 как возвращаемое значение.

Ответ 3

"!" Значит "нет", поэтому, когда вы размещаете его перед командой и запускаете "$?" Он будет эхом 1. Что означает, что он потерпел неудачу вместо 0, что означает успех.