Как определяется статус возврата при присвоении переменной?

ThoerX Forum Проверьте устройство с помощьюfdisk -l

Partition the device as following :-
fdisk /dev/sda
d - Delete old partitions
n - New partition
select partition number
select start block
select end block
v - verify the new partition
w - write through

now fdisk - l should show /dev/sda1 with proper filesystem type (say ext4)
mount -t ext4 /dev/sda1 /myMountPoint

10
19.03.2016, 04:35
2 ответа

Он документирован (для POSIX) в Разделе 2.9.1 Простые команды базовой спецификации The Open Group.  Там много текста; я обращаю ваше внимание на последний абзац:

Если есть имя команды, выполнение должно продолжаться, как описано в Поиск и выполнение команды.  Если имя команды отсутствует, но команда содержит подстановку команды, команда завершается со статусом выхода последней выполненной подстановки команды.  В противном случае команда завершается с нулевым статусом выхода.

Так, например,

   Command                                         Exit Status
$ FOO=BAR                                   0 (but see also the note from icarus, below)
$ FOO=$(bar)                                Exit status from "bar"
$ FOO=$(bar) baz                            Exit status from "baz"
$ foo $(bar)                                Exit status from "foo"

Так работает и bash.  Но см. также раздел "Не все так просто" в конце.

phk, в своем вопросе Назначения подобны командам со статусом выхода, за исключением случаев, когда происходит замена команды?, предлагает

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

Это не такой уж ужасный способ взглянуть на это.  Грубая схема для определения статуса возврата простой команды (не содержащей ;, &, |, && или ||) такова:

  • Просканируйте строку слева направо, пока не достигнете конца или командного слова (обычно имени программы).
  • Если вы видите присвоение переменной, статус возврата для этой строки может быть просто 0.
  • Если вы видите подстановку команды - т.е. $(...) - возьмите статус выхода из этой команды.
  • Если вы достигли фактической команды (не в подстановке команды), возьмите статус выхода из этой команды.
  • Статус возврата для строки - последний встреченный номер.
    Замены команд как аргументы к команде, например, foo $(bar), не считаются; вы получаете статус выхода из foo.  Перефразируя нотацию phk, поведение здесь такое

    temporary_variable = EXECUTE("bar" )
    overall_exit_status = EXECUTE("foo", temporary_variable )
    

Но это небольшое упрощение.  Общий статус возврата из

A=$(cmd1)  B=$(cmd2)  C=$(cmd3)  D=$(cmd4)  E=mc2
- это статус выхода из cmd4.  Присвоение E=, которое происходит после присваивания D= не устанавливает общий статус выхода в 0.

icarus, в своем ответе на вопрос phk, поднимает важный вопрос: переменные могут быть установлены как readonly.  Предпоследний абзац в Разделе 2.9.1 стандарта POSIX гласит:

Если любое из присваиваний переменной пытается присвоить значение переменной для которой в текущей среде оболочки установлен атрибут readonly (независимо от того, выполняется ли присвоение в этом окружении), произойдет ошибка присвоения переменной.  Последствия этих ошибок см. в Последствия ошибок оболочки.

поэтому, если вы скажете

readonly A
C=Garfield A=Felix T=Tigger

что статус возврата равен 1.  Не имеет значения, если строки Garfield, Felix, и/или Tigger заменяются командными подстановками - но см. примечания ниже.

Раздел 2.8.1 Последствия ошибок оболочки содержит еще одну кучу текста, и таблица, и заканчивается

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

Некоторые детали имеют смысл, некоторые нет:

  • Присвоение A= иногда прерывает командную строку, как, по-видимому, указано в последнем предложении.  В приведенном выше примере C установлено на Garfield, но T не установлено. (и, конечно, не задан A).
  • Similarly, C=$(cmd1) A=$(cmd2) T=$(cmd3) выполняет cmd1 но не cmd3.
    Но, в моих версиях bash (которые включают 4.1.X и 4.3.X), он выполняет cmd2.  (Кстати, это еще больше подрывает интерпретацию phk о том. что значение выхода из присваивания применяется перед правой частью присваивания.)

Но вот сюрприз:

В моих версиях bash,

readonly A
C=something A=something T=something cmd0

does execute cmd0.  В частности,

C=$(cmd1)   A=$(cmd2)   T=$(cmd3)   cmd0
выполняет cmd1 и cmd3 но не cmd2.  (Обратите внимание, что это противоположно его поведению при отсутствии команды).  И он устанавливает T (а также C) в окружение cmd0.  Интересно, не является ли это ошибкой в bash?

Не все так просто:

В первом абзаце этого ответа говорится о "простых командах".  В спецификации сказано:

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

Это утверждения, подобные тем, что приведены в моем первом блоке примеров:

$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)

первые три из них включают присваивание переменных, а последние три включают подстановки команд.

Но некоторые присваивания переменных не так просты.  bash(1) говорит:

Операторы присваивания могут также появляться в качестве аргументов для alias, declare, typeset, export, readonly, и local встроенные команды (команды declaration).

Для export, спецификация POSIX гласит:

EXIT STATUS

    0
      Все операнды имен были успешно экспортированы.
    >0
      По крайней мере одно имя не удалось экспортировать, или была указана опция -p и произошла ошибка.

И POSIX не поддерживает local, но bash(1) говорит:

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

Читая между строк, мы видим, что такие команды декларации, как

export FOO=$(bar)

и

local FOO=$(bar)

больше похожи на

foo $(bar)

постольку, поскольку они игнорируют статус выхода из bar и выдают статус выхода, основанный на главной команде (export, local или foo).  Таким образом, мы имеем странности вроде

   Command                                           Exit Status
$ FOO=$(bar)                                    Exit status from "bar"
                                                  (unless FOO is readonly)
$ export FOO=$(bar)                             0 (unless FOO is readonly,
                                                  or other error from “export”)
$ local FOO=$(bar)                              0 (unless FOO is readonly,
                                                  statement is not in a function,
                                                  or other error from “local”)

которые мы можем продемонстрировать с помощью

$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY   = $FRIDAY, status = $?"
FRIDAY   = Fri, May 04, 2018  8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0

и

myfunc() {
    local x=$(echo "Foo"; true);  echo "x = $x -> $?"
    local y=$(echo "Bar"; false); echo "y = $y -> $?"
    echo -n "BUT! "
    local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}

$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1

К счастью, ShellCheck отлавливает ошибку и поднимает SC2155, который советует изменить

export foo="$(mycmd)"

на

foo=$(mycmd)
export foo

и

local foo="$(mycmd)"

на

local foo
foo=$(mycmd)
19
27.01.2020, 20:01

Это задокументировано в Bash ( LESS = + / '^ SIMPLE COMMAND EXPANSION' bash ]):

Если после раскрытия осталось имя команды .... В противном случае команда завершается. ... Если подстановок команд не было, команда завершается с нулевым статусом.

Другими словами (мои слова):

Если после раскрытия не осталось имени команды и подстановки команд не выполнялись, командная строка завершается с нулевым статусом .

2
27.01.2020, 20:01

Теги

Похожие вопросы