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

10

Смотрите следующие примеры и их вывод в оболочках POSIX:

  1. false;echo $?или false || echo 1:1
  2. false;foo="bar";echo $?или foo="bar" && echo 0:0
  3. foo=$(false);echo $?или foo=$(false) || echo 1:1
  4. foo=$(true);echo $?или foo=$(true) && echo 0:0

Как указано в ответе с наибольшим количеством голосов на /programming/6834487/what-is-the-variable-in-shell-scripting :

$? используется для поиска возвращаемого значения последней выполненной команды.

Это, вероятно, немного вводит в заблуждение в этом случае, поэтому давайте получим определение POSIX, которое также приводится в сообщении из этой темы:

? Расширяется до десятичного состояния выхода самого последнего конвейера (см. Конвейеры).

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

Я вижу, как это поведение имеет смысл с практической точки зрения, но мне кажется несколько необычным, что само назначение будет учитываться в этом порядке. Может быть, чтобы прояснить, почему это странно для меня, давайте предположим, что назначение было функцией:

ASSIGNMENT( VARIABLE, VALUE )

тогда foo="bar"будет

ASSIGNMENT( "foo", "bar" )

и foo=$(false)было бы что-то вроде

ASSIGNMENT( "foo", EXECUTE( "false" ) )

Это означает, что EXECUTEзапускается сначала, а только потом ASSIGNMENT запускается, но здесь все равно имеет значение EXECUTEстатус.

Правильно ли я оцениваю или я что-то недопонимаю? Это правильные причины, по которым я считаю это поведение "странным"?

PHK
источник
1
Извините, но мне непонятно, что вы находите странным.
Кусалананда
1
@Kusalananda Может быть, это поможет вам сказать, что это началось с того, что я задал себе вопрос: «Почему false;foo="bar";echo $?всегда возвращается 0, когда была последняя действительная команда falseЭто в основном то, что назначения ведут себя особенным, когда дело доходит до кодов выхода. Их код выхода всегда равен 0, за исключением случаев, когда это происходит не из-за чего-то, что выполнялось как часть правой части назначения.
phk
1
Связанный: stackoverflow.com/questions/20157938/…
Кусалананда

Ответы:

10

Статус выхода для назначений странный . Наиболее очевидный способ провала назначения - если помечена целевая переменная readonly.

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Обратите внимание, что ни истинные, ни ложные пути оператора if не были приняты, при неудачном назначении выполнение всего оператора не прекращалось. bash в режиме POSIX, а ksh93 и zsh прервут выполнение сценария, если назначение не выполнено.

Чтобы процитировать стандарт POSIX на этом :

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

Это именно та часть грамматики оболочки, которая используется в

 foo=$(err 42)

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

Икар
источник
1
Чтобы добавить к вашему ответу, вот ответ из другой ветки, где цитируется более новый стандарт POSIX, вывод в основном такой же: unix.stackexchange.com/a/270831/117599
phk
4

Ты говоришь,

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

Это не ужасный взгляд на это. Но это небольшое упрощение. Общий статус возврата от

A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
это статус выхода из . Задание , которое происходит после того , как задание не устанавливает статус выхода в целом 0.cmd4E=D=

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

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

Несмотря на то, что Aон доступен только для чтения, bash выполняет подстановку команд справа от A=- и затем прерывает команду, потому что только для Aчтения. Это также противоречит вашей интерпретации того, что выходное значение присвоения применяется перед правой стороной присвоения.

G-Man говорит: «Восстанови Монику»
источник