Чому я не можу надрукувати змінну, яку я бачу у висновку env?


9

Мені цікаво встановити змінні середовища для одного екземпляра оболонки з іншого. Тому я вирішив зробити кілька досліджень. Після прочитання ряду на питання про це я вирішив перевірити його.

Я породив два снаряди А і В (PID 420), обидва бігали zsh. З оболонки AI вибігли наступні.

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

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

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

Чому вихід echo $FOOпорожній?


EDIT

Після вступу в коментарях я вирішив зробити трохи більше тестування. Результати можна побачити в таблицях нижче. У першому стовпці знаходиться оболонка, в яку 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'(використовуєте одинарні лапки!) Замість цього? Ви бачите це тоді?
користувач1934428

Правильне значення друкується з нової додаткової оболонки (також тестується на баш-дитину). Очевидно, що навколишнє середовище якось стійке, оскільки дитина може його успадкувати, але чому батько не шанує цього?
rlf

3
Це те, що я думав. Я здогадуюсь, що в оболонці є десь таблиця символів змінних, деякі з них позначені як "експортовані", що означає, що після відкриття нижньої оболонки вони поміщаються в оточення дочірнього процесу. Спочатку (коли запускається оболонка) змінні з тогочасного середовища копіюються в таблицю символів (звичайно також як "експортовані" змінні). Коли ви змінюєте середовище, оболонку не помічають, щоб оновити свою таблицю символів, але дочірні процеси (як env) бачать змінене середовище.
користувач1934428

2
Я протестував на Ubuntu 16.04 з zsh 5.1.1 та bash 4.3.48 (1), і здається, що встановлення змінної середовища для zshGDB не робить його видимим як змінної оболонки, а робить причиною передачі його дочірнім процесам (як ви помічали), встановивши параметр "дій", 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's main()is int main(int argc, char* argv[], char* envp[])bashвідображає змінні з цих оточуючих envp[]), а zsh' s є int main(int argc, char* argv[])і zshотримує змінні environзамість цього. setenv()змінює, environале не може змінювати envp[]на місці (лише для читання в декількох системах, а також рядки, на які вказують вказівники).

У будь-якому випадку, після завантаження оболонки environпри запуску, використання setenv()було б малоефективним, оскільки оболонка більше не використовує environ(або getenv()) після цього.

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