Призначення - це як команди зі статусом виходу, за винятком випадків, коли є заміна команд?


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має значення статус.

Я правильний у своїй оцінці чи щось нерозумію / пропускаю? Чи є ці правильні причини, коли я сприймаю цю поведінку як "дивну"?


1
Вибачте, але мені незрозуміло, що вам здається дивним.
Kusalananda

1
@Kusalananda Можливо, це допоможе вам сказати, що почалося з мене, запитуючи себе: "Чому false;foo="bar";echo $?завжди повертається 0, коли була остання реальна команда, що виконувалась false?" По суті, присвоєння поводяться особливо, коли мова йде про вихідні коди. Їх код виходу завжди 0, за винятком випадків, коли це не через те, що було частиною правої частини завдання.
phk

Відповіді:


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=

Крім того, як вказує icarus , змінні можуть бути встановлені як прочитані. Розглянемо наступну варіацію на прикладі ікаруса:

$ 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читається тільки. Це додатково суперечить вашій інтерпретації, що значення вихідного завдання застосовується перед правою частиною завдання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.