Почему не могут сохраняться переменные окружения, установленные в Python?

Я надеялся написать сценарий python для создания некоторых подходящих переменных среды, запустив сценарий в любом каталоге, в котором я буду выполнять некоторый код моделирования, и я прочитал, что я не могу написать сценарий, чтобы эти переменные env сохранялись в терминал Mac OS. Итак, две вещи:

Это правда?

и

Похоже, это было бы полезным занятием; почему это вообще невозможно?


person physicsmichael    schedule 03.04.2009    source источник
comment
Дубликат: stackoverflow.com/questions/488366/, stackoverflow.com/questions/235435/   -  person S.Lott    schedule 04.04.2009
comment
Это больше похоже на то, что это было бы полезно. Почему это невозможно?   -  person physicsmichael    schedule 04.04.2009
comment
Но это не важно. Если я сказал, что это юридическое ограничение, как это что-то изменит? Вы все еще не можете этого сделать. Если бы я сказал, что это элемент методистской книги дисциплины, как это что-то изменит? Вы все еще не можете этого сделать.   -  person S.Lott    schedule 04.04.2009
comment
@ vgm64 Я думаю, что если вы уточните природу переменных среды, которые вы пытаетесь установить, мы сможем коллективно найти хорошее альтернативное решение, используя простые сценарии оболочки. Python мешает, потому что интерпретатор - это отдельный процесс со своим собственным env. Вам придется форкнуть от Python ...   -  person Joe Holloway    schedule 04.04.2009
comment
Почему я не могу воровать в магазине? и Могу ли я воровать в магазине? это два отдельных вопроса. Я спрашиваю первое, но Бенсон дал хороший технический ответ (и, что удивительно, решение!).   -  person physicsmichael    schedule 04.04.2009
comment
@ jholloway7 Вы правы. Моя ситуация требует, чтобы переменная среды была установлена ​​для любого каталога, в котором я буду проводить некоторый анализ. Моим решением будет создание псевдонима: alias geant4cwd = 'export $ G4WORKDIR = pwd' Идеальное решение.   -  person physicsmichael    schedule 04.04.2009
comment
Чтобы изменить envvars в другом процессе (например, в родительской оболочке), в Linux есть ответ ›unix.stackexchange.com/questions/38205/   -  person jsbueno    schedule 19.08.2015


Ответы (8)


Вы не можете сделать это с помощью Python, но некоторые хитрые трюки с bash могут сделать нечто подобное. Основная причина такова: переменные среды существуют в пространстве памяти для каждого процесса. Когда новый процесс создается с помощью fork (), он наследует переменные среды своего родителя. Когда вы устанавливаете переменную среды в своей оболочке (например, bash) следующим образом:

export VAR="foo"

Вы говорите bash установить для переменной VAR в его пространстве процесса значение «foo». Когда вы запускаете программу, bash использует fork (), а затем exec () для запуска программы, поэтому все, что вы запускаете из bash, наследует переменные среды bash.

Теперь предположим, что вы хотите создать команду bash, которая устанавливает некоторую переменную среды DATA с содержанием из файла в вашем текущем каталоге с именем «.data». Во-первых, вам нужна команда для получения данных из файла:

cat .data

Это печатает данные. Теперь мы хотим создать команду bash для установки этих данных в переменной среды:

export DATA=`cat .data`

Эта команда берет содержимое .data и помещает его в переменную среды DATA. Теперь, если вы поместите это в команду псевдонима, у вас будет команда bash, которая устанавливает переменную среды:

alias set-data="export DATA=`cat .data`"

Вы можете поместить эту команду псевдонима в файлы .bashrc или .bash_profile в своем домашнем каталоге, чтобы эта команда была доступна в любой новой запускаемой оболочке bash.

person Benson    schedule 03.04.2009

Один из способов решения этой проблемы - вывести export команд, и родительская оболочка должна это оценить.

thescript.py:

import pipes
import random
r = random.randint(1,100)
print("export BLAHBLAH=%s" % (pipes.quote(str(r))))

..и псевдоним bash (то же самое можно сделать в большинстве оболочек .. даже tcsh!):

alias setblahblahenv="eval $(python thescript.py)"

Использование:

$ echo $BLAHBLAH

$ setblahblahenv
$ echo $BLAHBLAH
72

Вы можете вывести любой произвольный код оболочки, включая несколько команд, например:

export BLAHBLAH=23 SECONDENVVAR='something else' && echo 'everything worked'

Просто не забудьте быть осторожными, избегая любого динамически создаваемого вывода (для этого подходит модуль pipes.quote)

person dbr    schedule 04.04.2009
comment
Что, если мы хотим установить несколько переменных среды и предоставить некоторую обратную связь о выполнении, например об успехе или неудаче? - person chmike; 22.01.2013
comment
@chmike команда export позволяет использовать несколько переменных, например export BLAH=123 ANOTHER=321 ANDMORE=434. Для сообщения об успехе / неудаче вы можете вывести любую команду, и она будет выполнена (например, команда echo) - person dbr; 22.01.2013

Если вы установите переменные среды в скрипте Python (или в любом другом скрипте или программе), это не повлияет на родительскую оболочку.

Отредактируйте пояснение: Итак, ответ на ваш вопрос - да, это правда. Однако вы можете экспортировать из сценария оболочки и использовать его в качестве источника с помощью вызова точки

в fooexport.sh

export FOO="bar"

в командной строке

$ . ./fooexport.sh
$ echo $FOO
bar
person Stefano Borini    schedule 03.04.2009
comment
Итак, как насчет написания псевдонима, который 1) запускает сценарий python в cwd, который создает сценарий оболочки с переменными среды, которые мне нужны на основе cwd, и 2) вызывает этот сценарий, как вы написали? - person physicsmichael; 04.04.2009
comment
Стефано, я проверил приложение Papers, упомянутое на ForTheScience.org. Рад, что ты решил ответить на мой вопрос! Я подозреваю, что если я получу студенческую скидку, она определенно окупится, так что спасибо. - person physicsmichael; 04.04.2009
comment
о да, псевдоним, как вы сказали, будет работать, потому что он вызывается вашей текущей оболочкой. Это определенно решение вашей проблемы. - person Stefano Borini; 04.04.2009

Это вообще невозможно. Новый процесс, созданный для python, не может повлиять на среду его родительского процесса. Родитель также не может повлиять на дочерний элемент, но родитель может настроить дочернюю среду как часть создания нового процесса.

Возможно, вы можете установить их в .bashrc, .profile или эквивалентном сценарии «запускается при входе в систему» ​​или «запускается в каждом новом сеансе терминала» в MacOS.

Вы также можете заставить python запустить программу моделирования с желаемой средой. (используйте параметр env для subprocess.Popen (http://docs.python.org/library/subprocess.html))

import subprocess, os
os.chdir('/home/you/desired/directory')
subprocess.Popen(['desired_program_cmd', 'args', ...], env=dict(SOMEVAR='a_value') )

Или вы могли бы заставить python записать сценарий оболочки, подобный этому, в файл с расширением .sh:

export SOMEVAR=a_value
cd /home/you/desired/directory
./desired_program_cmd

а затем chmod +x и запускать откуда угодно.

person Joe Koberg    schedule 03.04.2009
comment
Программное обеспечение для моделирования, которое я использую, требует, чтобы определенная переменная среды была установлена ​​вручную для любого каталога, в котором вы хотите провести анализ. Я считаю это ужасным подходом и думал исправить это, или хотя бы автоматизировать это с помощью Python. - person physicsmichael; 04.04.2009

Мне нравится использовать / usr / bin / env в сценарии оболочки, чтобы «обернуть» мою командную строку, когда я оказываюсь в аналогичных ситуациях:

#!/bin/bash

/usr/bin/env NAME1="VALUE1" NAME2="VALUE2" ${*}

Так что давайте назовем этот сценарий «myappenv». Я помещаю его в свой каталог $ HOME / bin, который есть в моем $ PATH.

Теперь я могу вызвать любую команду, используя эту среду, просто добавив "myappenv" как таковое:

myappenv dosometask -xyz

Другие опубликованные решения тоже работают, но это мое личное предпочтение. Одно из преимуществ состоит в том, что среда временная, поэтому, если я работаю в оболочке, измененная среда влияет только на вызываемую мной команду.

Измененная версия на основе новых комментариев

#!/bin/bash

/usr/bin/env G4WORKDIR=$PWD ${*}

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

person Joe Holloway    schedule 03.04.2009

Как ответил Бенсон, но лучший обходной путь - создать простую функцию bash для сохранения аргументов:

upsert-env-var (){ eval $(python upsert_env_var.py $*); }

Вы можете делать все, что хотите, в своем скрипте Python с аргументами. Чтобы просто добавить переменную, используйте что-то вроде:

var = sys.argv[1]
val = sys.argv[2]
if os.environ.get(var, None):
    print "export %s=%s:%s" % (var, val, os.environ[var])
else:
    print "export %s=%s" % (var, val)

Использование:

upsert-env-var VAR VAL
person Niall    schedule 13.09.2015

Как указывали другие, причина, по которой это не работает, заключается в том, что переменные среды живут в пространствах памяти для каждого процесса и поэтому умирают, когда процесс Python завершается.

Они указывают, что решением этой проблемы является определение псевдонима в .bashrc, чтобы делать то, что вы хотите, например:

alias export_my_program="export MY_VAR=`my_program`"

Однако есть еще один (немного хитрый) метод, который не требует от вас изменения .bachrc и наличия my_program в $PATH (или указания полного пути к нему в псевдониме). Идея состоит в том, чтобы запустить программу на Python, если она вызывается нормально (./my_program), и на Bash, если она получена (source my_program). (Использование source в сценарии не порождает новый процесс и, следовательно, не уничтожает переменные среды, созданные внутри.) Вы можете сделать это следующим образом:

my_program.py:

#!/usr/bin/env python3

_UNUSED_VAR=0
_UNUSED_VAR=0 \
<< _UNUSED_VAR
#=======================
# Bash code starts here
#=======================
'''
_UNUSED_VAR
export MY_VAR=`$(dirname $0)/my_program.py`
echo $MY_VAR
return
'''

#=========================
# Python code starts here
#=========================
print('Hello environment!')

Запустив это в Python (./my_program.py), первые 3 строки не сделают ничего полезного, а тройные кавычки закомментируют код Bash, позволяя Python нормально работать без каких-либо синтаксических ошибок из Bash.

Получив это в bash (source my_program.py), heredoc (<< _UNUSED_VAR) - это уловка, используемая для «комментирования» первой тройной кавычки, что в противном случае было бы синтаксической ошибкой. Сценарий возвращается, не дойдя до второй тройной кавычки, что позволяет избежать еще одной синтаксической ошибки. export присваивает результат выполнения my_program.py в Python из правильного каталога (заданного $(dirname $0)) переменной среды MY_VAR. echo $MY_VAR выводит результат в командную строку.

Пример использования:

$ source my_program.py
Hello environment!
$ echo $MY_VAR
Hello environment!

Однако сценарий по-прежнему будет делать все, что делал до этого, за исключением экспорта переменной среды, если выполняется нормально:

$ ./my_program.py
Hello environment!
$ echo $MY_VAR
                                <-- Empty line
person nijoakim    schedule 10.05.2020

Как отмечают другие авторы, при завершении процесса Python память выбрасывается. Но во время процесса python вы можете редактировать рабочую среду. Например:

>>> os.environ["foo"] = "bar"
>>> import subprocess
>>> subprocess.call(["printenv", "foo"])
bar
0
>>> os.environ["foo"] = "foo"
>>> subprocess.call(["printenv", "foo"])
foo
0
person macetw    schedule 20.04.2021