Из того, что я читал, помещение команды в скобки должно запустить ее в подоболочке, подобно запуску скрипта. Если это правда, как он видит переменную x, если x не экспортируется?
x=1
Запуск (echo $x)
в командной строке приводит к 1
Запуск echo $x
скрипта, как и ожидалось, ни к чему не приводит.
решение1
Подоболочка начинается как почти идентичная копия исходного процесса оболочки. Под капотом оболочка вызываетfork
системный вызов 1 , который создает новый процесс, код и память которого являются копиями 2 . Когда создается подоболочка, между ней и ее родителем очень мало различий. В частности, у них одинаковые переменные. Даже $$
специальная переменная сохраняет одно и то же значение в подоболочках: это идентификатор процесса исходной оболочки. Аналогично $PPID
и PID родителя исходной оболочки.
Несколько оболочек изменяют несколько переменных в подоболочке. Bash ≥4.0 устанавливает BASHPID
PID процесса оболочки, который изменяется в подоболочках. Bash, zsh и mksh устраивают так, $RANDOM
чтобы в родительской оболочке и подоболочке выдавались разные значения. Но за исключением встроенных особых случаев, подобных этим, все переменные имеют то же значение в подоболочке, что и в исходной оболочке, тот же статус экспорта, тот же статус «только для чтения» и т. д. Все определения функций, определения псевдонимов, параметры оболочки и другие настройки также наследуются.
Подоболочка, созданная с помощью , (…)
имеет те же дескрипторы файлов, что и ее создатель. Некоторые другие способы создания подоболочек изменяют некоторые дескрипторы файлов перед выполнением пользовательского кода; например, левая часть канала выполняется в подоболочке 3 со стандартным выводом, подключенным к каналу. Подоболочка также начинается с того же текущего каталога, той же маски сигналов и т. д. Одним из немногих исключений является то, что подоболочки не наследуют пользовательские ловушки: игнорируемые сигналы ( ) остаются игнорируемыми в подоболочке, но другие ловушки (trap '' SIGNAL
trap CODE
СИГНАЛ) сбрасываются до действия по умолчанию 4 .
Таким образом, подоболочка отличается от выполнения скрипта. Скрипт — это отдельная программа. Эта отдельная программа может по совпадению оказаться также скриптом, который выполняется тем же интерпретатором, что и родитель, но это совпадение не дает отдельной программе никакой особой видимости внутренних данных родителя. Неэкспортируемые переменные — это внутренние данные, поэтому, когда интерпретатор для дочернего скрипта оболочкиказнен, он не видит эти переменные. Экспортированные переменные, т.е. переменные окружения, передаются исполняемым программам.
Таким образом:
x=1
(echo $x)
печатает, 1
потому что подоболочка является копией оболочки, которая ее породила.
x=1
sh -c 'echo $x'
случается, что оболочка запускается как дочерний процесс оболочки, но x
на второй строке не имеет больше связей с x
на второй строке, чем в
x=1
perl -le 'print $x'
или
x=1
python -c 'print x'
1 Если только оболочка не оптимизирует разветвление, а эмулирует разветвление настолько, насколько это необходимо для сохранения поведения кода, который она выполняет. Ksh93 оптимизирует многое, другие оболочки в основном нет.
2 Семантически они являются копиями. С точки зрения реализации происходит много общего.
3 Для правой стороны это зависит от оболочки.
4 Если вы это протестируете, обратите внимание, чтовещи как$(trap)
может сообщать о ловушках оригинальной оболочки. Обратите внимание также, что многие оболочки имеют ошибки в угловых случаях, связанных с ловушками. Напримерниндзялджотмечает, что, начиная с bash 4.3, bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'
запускает ERR
ловушку из вложенной подоболочки в случае «двух подоболочек», но не ловушку ERR
из промежуточной подоболочки — set -E
опция должна распространять ERR
ловушку на все подоболочки, но промежуточная подоболочка оптимизирована и поэтому не может запустить свою ERR
ловушку.
решение2
Очевидно, да, как и говорится во всей документации, команда в скобках выполняется в подоболочке.
Подоболочка наследует копию всех переменных родителя. Разница в том, что любые изменения, которые вы делаете в подоболочке, не производятся также в родителе.
Страница руководства ksh делает это немного яснее, чем страница bash:
man ksh
:
Команда в скобках выполняется в подоболочке без удаления неэкспортированных переменных.
man bash
:
(
список)
list выполняется в среде подоболочки (см. СРЕДА ВЫПОЛНЕНИЯ КОМАНД ниже). Назначения переменных и встроенные команды, которые влияют на среду оболочки, не остаются в силе после завершения команды.
СРЕДА ВЫПОЛНЕНИЯ КОМАНД
Оболочка имеет среду выполнения, которая состоит из следующих элементов: [...] параметры оболочки, которые задаются путем назначения переменных [...].
Подстановка команд, команды, сгруппированные с помощью скобок, и асинхронные команды вызываются в среде подоболочки, которая является дубликатом среды оболочки, [...]