我希望通过在我将执行一些模拟代码的任何目录中运行脚本来编写一个 python 脚本来创建一些适当的环境变量,并且我已经读到我无法编写脚本来使这些环境变量持续存在mac 操作系统终端。所以有两件事:
这是真的?
和
看起来这将是一件有用的事情;为什么一般来说不可能?
我希望通过在我将执行一些模拟代码的任何目录中运行脚本来编写一个 python 脚本来创建一些适当的环境变量,并且我已经读到我无法编写脚本来使这些环境变量持续存在mac 操作系统终端。所以有两件事:
这是真的?
和
看起来这将是一件有用的事情;为什么一般来说不可能?
你不能从 python 中做到这一点,但一些聪明的 bash 技巧可以做类似的事情。基本推理是这样的:环境变量存在于每个进程的内存空间中。当使用 fork() 创建一个新进程时,它会继承其父进程的环境变量。当您在 shell(例如 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 shell 中可用。
一种解决方法是输出export
命令,并让父 shell 对此进行评估。
thescript.py
:
import pipes
import random
r = random.randint(1,100)
print("export BLAHBLAH=%s" % (pipes.quote(str(r))))
..和 bash 别名(在大多数 shell 中都可以这样做。甚至 tcsh!):
alias setblahblahenv="eval $(python thescript.py)"
用法:
$ echo $BLAHBLAH
$ setblahblahenv
$ echo $BLAHBLAH
72
您可以输出任意 shell 代码,包括多个命令,例如:
export BLAHBLAH=23 SECONDENVVAR='something else' && echo 'everything worked'
请记住要小心转义任何动态创建的输出(该pipes.quote
模块对此很有用)
如果您在 python 脚本(或任何其他脚本或程序)中设置环境变量,它不会影响父 shell。
编辑说明:所以你的问题的答案是肯定的,这是真的。但是,您可以从 shell 脚本中导出并使用点调用来获取它
在 fooexport.sh
export FOO="bar"
在命令提示符下
$ . ./fooexport.sh
$ echo $FOO
bar
这通常是不可能的。为 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 将这样的 shell 脚本写到一个带有.sh
扩展名的文件中:
export SOMEVAR=a_value
cd /home/you/desired/directory
./desired_program_cmd
然后chmod +x
它并从任何地方运行它。
当我发现自己处于类似情况时,我喜欢做的是在 shell 脚本中使用 /usr/bin/env 来“包装”我的命令行:
#!/bin/bash
/usr/bin/env NAME1="VALUE1" NAME2="VALUE2" ${*}
所以让我们称这个脚本为“myappenv”。我把它放在我的 $PATH 中的 $HOME/bin 目录中。
现在我可以使用该环境调用任何命令,只需在前面加上“myappenv”即可:
myappenv dosometask -xyz
其他发布的解决方案也可以,但这是我个人的偏好。一个优点是环境是瞬态的,所以如果我在 shell 中工作,那么只有我调用的命令会受到改变的环境的影响。
基于新评论的修改版本
#!/bin/bash
/usr/bin/env G4WORKDIR=$PWD ${*}
您也可以将这一切都包装在别名中。我更喜欢包装脚本方法,因为我也倾向于在其中准备其他环境,这使我更容易维护。
正如 Benson 所回答的那样,但最好的解决方法是创建一个简单的 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
正如其他人指出的那样,这不起作用的原因是环境变量存在于每个进程的内存空间中,因此在 Python 进程退出时会死掉。
他们指出,解决这个问题的方法是定义一个别名.bashrc
来做你想做的事情,比如:
alias export_my_program="export MY_VAR=`my_program`"
但是,还有另一种(有点骇人听闻的)方法不需要您修改.bachrc
,也不需要您拥有(或在别名中指定它的完整路径)my_program
。$PATH
这个想法是,如果程序被正常调用,则在 Python 中运行程序./my_program
(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
(
在 bash ( source my_program.py
) 中获取此信息,heredoc ( << _UNUSED_VAR
) 是一种用于“注释掉”第一个三引号的 hack,否则会出现语法错误。脚本在到达第二个三引号之前返回,避免了另一个语法错误。将Pythonexport
中运行的结果从正确的目录(由 给出)分配给环境变量。在命令行上打印结果。my_program.py
$(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
正如其他作者所指出的,当 Python 进程退出时,内存会被丢弃。但是在python过程中,可以编辑运行环境。例如:
>>> os.environ["foo"] = "bar"
>>> import subprocess
>>> subprocess.call(["printenv", "foo"])
bar
0
>>> os.environ["foo"] = "foo"
>>> subprocess.call(["printenv", "foo"])
foo
0