Подтвердить что ты не робот

Переменные среды в Python в Linux

Доступ к переменным среды Python не точно отражает представление операционной системы в среде процессов.

os.getenv и os.environ не работают должным образом в определенных случаях.

Есть ли способ правильно получить среду запущенного процесса?


Чтобы продемонстрировать, что я имею в виду, возьмите две примерно эквивалентные программы (первая в C, другая в python):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]){
    char *env;
    for(;;){
        env = getenv("SOME_VARIABLE");
        if(env)
            puts(env);
        sleep(5);
    }
}

import os
import time
while True:
    env = os.getenv("SOME_VARIABLE")
    if env is not None:
        print env
    time.sleep(5)

Теперь, если мы запустим программу C и приложим к запущенному процессу с помощью gdb и принудительно изменим среду под капотом, сделав что-то вроде этого:

(gdb) print setenv("SOME_VARIABLE", "my value", 1)
[Switching to Thread -1208600896 (LWP 16163)]
$1 = 0
(gdb) print (char *)getenv("SOME_VARIABLE")
$2 = 0x8293126 "my value"

то вышеупомянутая программа C начнет извергать "мое значение" каждые 5 секунд. Однако вышеупомянутая программа python не будет.

Есть ли способ заставить программу python функционировать как программа C в этом случае?

(Да, я понимаю, что это очень неявное и потенциально опасное действие для выполнения на запущенном процессе)

Кроме того, я в настоящее время использую python 2.4, это может быть исправлено в более поздней версии python.

4b9b3361

Ответ 1

Это очень хороший вопрос.

Оказывается, модуль os инициализирует os.environ значением posix .environ, который установлен на переводчик запускается. Другими словами, стандартная библиотека не обеспечивает доступ к функции getenv.

Это случай, когда, вероятно, было бы безопасно использовать ctypes в unix. Поскольку вы будете называть ультра-стандартную функцию libc.

Ответ 2

Вы можете использовать ctypes, чтобы сделать это довольно просто:

>>> from ctypes import CDLL, c_char_p
>>> getenv = CDLL("libc.so.6").getenv
>>> getenv.restype = c_char_p
>>> getenv("HOME")
'/home/glyph'

Ответ 3

Другая возможность заключается в том, чтобы вместо этого использовать pdb или другой отладчик python и изменить os.environ на уровне python, а не на уровне C. Вот небольшой рецепт, который я опубликовал, чтобы прервать запущенный процесс python и предоставить доступ к консоли python при получении сигнала. В качестве альтернативы просто вставьте pdb.set_trace() в какой-то момент вашего кода, который вы хотите прервать. В любом случае просто запустите оператор "import os; os.environ['SOME_VARIABLE']='my_value'", и вы должны обновить его до уровня python.

Я не уверен, что это также обновит среду C с помощью setenv, поэтому, если у вас есть C-модули с использованием getenv, вам может понадобиться сделать еще одну работу, чтобы синхронизировать это.

Ответ 4

Я не верю, что многие программы ВСЕГДА ожидают изменения внешней среды, поэтому загрузка копии пройденной среды при запуске эквивалентна. Вы просто наткнулись на выбор реализации.

Если вы видите все значения при запуске и putenv/setenv из вашей программы, я не думаю, что что-то нужно беспокоить. Есть более чистые способы передачи обновленной информации для запуска исполняемых файлов.

Ответ 5

Глядя на исходный код Python (2.4.5):

  • Модули/posixmodule.c получает окружение в преобразовании(), которое запускается при запуске (см. INITFUNC) и сохраняет среду в модуле, специфичном для платформы (nt, os2 или posix)

  • Lib/os.py просматривает sys.builtin_module_names и импортирует все символы из posix, nt или os2

Так что да, это решено при запуске. os.environ здесь не поможет.

Если вы действительно хотите это сделать, то наиболее очевидным подходом, который приходит на ум, является создание собственного пользовательского Cython-модуля python с getenv, который всегда вызывает системный вызов.