Вы можете попробовать дерево:
tree -d -h --du
-d только печатать каталоги, -du использование диска, -h удобный для человека вывод
Результат как true | echo "$?"
, так и false | echo "$?"
вводит в заблуждение. Содержимое "$?"
будет установлено до того, как передаст команду false
в команду echo
.
Для выполнения этих строк bash устанавливает конвейер команд. Конвейер настроен, затем команды запущены параллельно. Итак, в вашем примере:
true; echo "$?" # 0
false; echo "$?" # 1
true | echo "$?" # 0
совпадает с:
true
echo "$?" # 0
false
echo "$?" # 1
echo "$?" # 0
true
не выполняется до тех пор, пока echo $?
не выполняется одновременно.
Это связано с тем, что обе части конвейера выполняются параллельно, поэтому команда false
еще не завершена (и установите $?
), когда команда echo
уже начинает печатать $?
, что является все еще результат предыдущей команды ; в вашем случае, скорее всего, последний echo
выполнил (и, вероятно, успешно ).
В false | echo $?
$?
не является статусом выхода false
, потому что$?
расширяется до десятичного статуса выхода самого последнего конвейера [1], а не самой последней команды , подоболочки или дочернего процесса . В false | echo $?
, false
не конвейер, а просто его часть. Простая полная команда, такая как false;
, является конвейером, даже если не содержит никакого |
.
Предполагая, что set -o pipefail
включен и состояние выхода false | echo $?
будет таким же, как у false
, $?
также не может быть выходом из состояния текущего конвейера, поскольку текущий конвейер еще не вышел к тому времени, когда он повторяет это.
Не имеет значения порядок, в котором две стороны конвейера (, которые всегда выполняются параллельно ), запускаются или завершаются, а также то, выполняется ли echo $?
фактически в дочернем процессе или подоболочке.
FWIW, когда в подоболочке переменные и другие параметры всегда расширяются в контексте текущей подоболочки :, если false | echo $?
реализовано путем разветвления отдельных процессов для обеих сторон конвейера (, что имеет место в bash, но не во всех оболочках ), $?
будут расширены в дочернем процессе, после fork()
и, возможно, после левой стороны конвейера вышел [2].
And how could I force a failure in a pipe, and get 1 thereafter?
Вы используете set -o pipefail
, который поддерживается в bash, zsh и ksh и должен быть включен в будущую версию стандарта. Но, как объяснялось выше, это влияет на значение$?
только после выхода из конвейера, а не в самом конвейере.
[1] 2.5.2 Специальные параметры в стандарте SUSv4. Будет ли подстановка команд считаться конвейером в этом контексте, зависит от оболочки; в большинстве старых и современных оболочек :; echo `exit 13` $?
будет печатать 13
, но в некоторых (, таких как dash, yash или pdksh -, производных ), он будет печатать 0
.
[2] Простой пример bash, который может помочь понять, что это echo $BASHPID >&2 | echo $BASHPID >&2 | echo $BASHPID >&2
; переменная BASHPID
является не расширенной до того, как будут настроены и запущены 3 дочерних процесса. Специальные параметры, такие как $?
, в этом отношении не являются особыми; они расширяются, как и любые другие переменные.