Почему я не могу напечатать переменную, которую вижу в выводе env?

9

Я заинтересован в установке переменных среды одного экземпляра оболочки из другого. Поэтому я решил провести небольшое исследование. После прочтения ряда на вопросы о этом я решил проверить его.

Я породил две оболочки A и B (PID 420), обе работают zsh. Из оболочки ИИ запускается следующее.

sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach

При запуске из оболочки B я envвижу, что переменная FOO действительно установлена ​​со значением bar. Это заставляет меня думать, что FOO была успешно инициализирована в среде оболочки B. Однако, если я пытаюсь напечатать FOO, я получаю пустую строку, подразумевая, что она не установлена. Мне кажется, что здесь есть противоречие.

Это было протестировано как на моей собственной системе Arch GNU / Linux, так и на виртуальной машине Ubuntu. Я также проверил это, bashкогда переменная даже не отображалась в env. Это, хотя и разочаровывает меня, имеет смысл, если оболочка кэширует копию своего окружения во время появления и использует только ее (что было предложено в одном из связанных вопросов). Это все еще не отвечает, почему zshможно увидеть переменную.

Почему вывод echo $FOOпустой?


РЕДАКТИРОВАТЬ

После ввода комментариев я решил провести еще несколько тестов. Результаты можно увидеть в таблицах ниже. В первом столбце находится оболочка, в которую FOOбыла введена переменная. Первая строка содержит команду, вывод которой можно увидеть под ней. Переменной FOOвводили с помощью: sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'. Команды, специфичные для zsh:, zsh -c '...'также были протестированы с использованием bash. Результаты были идентичны, их вывод был опущен для краткости.

Arch GNU / Linux, zsh 5.3.1, bash 4.4.12 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ubuntu 16.04.2 LTS, zsh 5.1.1, bash 4.3.48 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Вышеуказанное, по-видимому, подразумевает, что результаты не зависят от распределения. Это не говорит мне намного больше чем zshи bashобрабатывает установку переменных по-другому. Кроме того, export FOOимеет очень различное поведение в этом контексте в зависимости от оболочки. Надеемся, что эти тесты могут кое-что прояснить кому-то еще.

ДКП
источник
Что произойдет, если zsh -c 'echo $FOO'вместо этого вы сделаете (используйте одинарные кавычки!)? Ты видишь это тогда?
user1934428
Правильное значение выводится из новой вложенной оболочки (также проверено на bash child). Очевидно, что среда как-то постоянна, так как ребенок может ее унаследовать, но почему родитель не соблюдает ее?
ДКП
3
Это то, о чем я думал. Я предполагаю, что оболочка имеет где-то таблицу символов переменных, некоторые из них помечены как «экспортированные», что означает, что при открытии подоболочки они помещаются в среду дочернего процесса. Первоначально (когда запускается оболочка) переменные из среды в это время копируются в таблицу символов (конечно, также как «экспортированные» переменные). Когда вы изменяете среду, оболочка не замечает обновления своей таблицы символов - но дочерние процессы (например env) видят измененную среду.
user1934428
2
Я тестировал на Ubuntu 16.04 с zsh 5.1.1 и bash 4.3.48 (1), и кажется, что установка переменной среды для zshв GDB не делает ее видимой в качестве переменной оболочки, но заставляет ее передаваться дочерним процессам (как вы наблюдали), при установке один для bash действительно сделать его видимым в качестве переменной оболочки , но это не причина, чтобы это было передано дочернему процессу! Похоже, что zsh и bash используют разные стратегии для управления переменными, при этом zsh отслеживает переменные, не относящиеся к среде, и bash хранит все в своей среде, которую он очищает при запуске дочернего (не подоболочечного) дочернего элемента.
Элия ​​Каган
@EliahKagan, интересно; Вы должны опубликовать это как ответ. Мне также интересно , если это имеет значение , если вы работаете export FOOв bash?
Wildcard

Ответы:

2

Большинство оболочек не используют getenv()/ setenv()/ putenv()API.

После запуска они создают переменные оболочки для каждой переменной среды. Они будут храниться во внутренних структурах, которые должны нести другую информацию, например, экспортируется ли переменная, только для чтения ... Они не могут использовать библиотеки libc environдля этого.

Точно так же, и по этой причине они не будут использовать execlp(), execvp()чтобы выполнять команды , но вызвать execve()системный вызов напрямую, вычисление envp[]массива на основе списка своих экспортируемых переменных.

Таким образом, в вашем случае gdbвам нужно добавить запись в эту внутреннюю таблицу переменных оболочки или, возможно, вызвать нужную функцию, которая заставила бы ее интерпретировать export VAR=valueкод для ее самостоятельного обновления.

Относительно того , почему вы видите разницу между bashи , zshкогда вы звоните setenv()в gdb, я подозреваю , что это потому , что вы вызываете setenv()перед инициализирует оболочки, например , при входе main().

Вы заметите, bashчто main()есть int main(int argc, char* argv[], char* envp[])bashотображает переменные из этих переменных envp[]), в то время как zshэто int main(int argc, char* argv[])и zshполучает переменные environвместо этого. setenv()изменяет, environно не может изменять envp[]на месте (только для чтения в нескольких системах, а также строки, на которые указывают эти указатели).

В любом случае, после того, как оболочка прочитала environпри запуске, использование setenv()будет неэффективным, поскольку оболочка больше не использует environ(или getenv()) впоследствии.

Стефан Шазелас
источник