【问题标题】:Execute bash script that defines environment variables using Python执行使用 Python 定义环境变量的 bash 脚本
【发布时间】:2017-09-15 22:54:41
【问题描述】:

我正在使用下面的代码来执行一个定义了很多环境变量的 bash 脚本:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import subprocess

# Code used to get the value of variable before the call to my script
command = "echo $MYDIR"
ps = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
my_dir = ps.communicate()[0]
print "my_dir", my_dir

subprocess.Popen(["/bin/sh", "/home/user/env_file"])

# Code used to get the value of established variable
command = "echo $MYDIR"
ps = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
my_dir = ps.communicate()[0]
print "my_dir", my_dir

我的/home/user/env_file的内容是下一个:

#!/bin/bash

export MYDIR=/home/user
export MYDIR2=/home/user2
export MYDIR3=/home/user3
export MYDIR4=/home/user4
export MYDIR5=/home/user5
export MYDIR6=/home/user6
export MYDIR7=/home/user7
export MYDIR8=/home/user8
export MYDIR9=/home/user9
export MYDIR10=/home/user10
export MYDIR11=/home/user11
export MYDIR12=/home/user12
export MYDIR13=/home/user13
export MYDIR14=/home/user14
export MYDIR15=/home/user15
export MYDIR16=/home/user16

当我执行 Python 代码时,我没有得到 my_dir 变量的任何值。我该怎么做?

最好的问候。

【问题讨论】:

  • 一个程序只能改变它自己及其子程序的环境变量,不能改变启动它的程序的环境变量。因此,从 Python 解释器启动的任何 bash 脚本都不能为该 Python 解释器设置变量。
  • 由于子进程无法更改其父环境,因此您无法执行脚本来执行此操作。你需要编写 Python 代码来读取脚本,解析赋值,然后赋值给os.environ
  • @Barmar,...好吧。我不愿鼓励某人编写自己的 shell 解析器——在这条路上有很多陷阱。我目前正在开始在 shell 中实现一个答案,该答案提供一个任意脚本并将在执行结束时存在的所有环境变量写入到标准输出中以 NUL 分隔的形式。
  • 为什么要做外部shell脚本?为什么不直接读取 python 脚本中变量的名称和值呢?你真的需要 shell 提供的所有功能吗(从引用问题开始,到命令替换之类的东西结束)?
  • @CharlesDuffy 我的评论仅适用于问题中的文字赋值。如果有任何shell逻辑,那就有问题了。

标签: python linux variables subprocess environment


【解决方案1】:

进程影响其父环境的唯一方法(不存在滥用开发/调试设施的丑陋黑客)是该父进程的直接参与(这就是为什么运行eval "$(ssh-agent)" -- evaling 它的输出是,在这种情况下,需要父母的合作才能让ssh-agent在启动它的进程中设置环境变量)。考虑以下外壳包装器:

#!/usr/bin/env bash
# Save this script as "envvar-extraction-wrapper"
exec {orig_stdout}>&1 # create a backup of stdout
exec >&2              # and then use stderr while sourcing the child script

source "$@"           # run arbitrary script with arbitrary arguments *in this shell*
                      # note that this means that if it runs "exit", we abort prematurely

while read -r varname; do
  printf '%s\0%s\0' "$varname" "${!varname}" >&$orig_stdout
done < <(compgen -e)

...从以下 Python 调用:

import subprocess, itertools, os

ps = subprocess.Popen(['envvar-extraction-wrapper', '/home/user/env_file'],
                      stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdout, stderr) = ps.communicate()
items_iter = iter(stdout.split('\0'))
for (k,v) in itertools.izip(items_iter, items_iter):
    os.environ[k]=v

【讨论】:

    【解决方案2】:

    bash 脚本 env_file 在其自己的进程中执行,因此此处对环境变量的更改不会反映回 python 脚本的进程。如果您可以重新设计脚本以避免这种情况,那将是最好的。否则,我们必须采取一些技巧,这里就是其中之一。

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    import os
    import subprocess
    import tempfile
    
    env_file = "/tmp/env_file"
    with open(env_file) as f:
        original_script_contents = f.read()
    
    # Duplicate the env_file and add an echo statement at the end
    with tempfile.NamedTemporaryFile() as temp_script:
        temp_script.write(original_script_contents)
        temp_script.write('\necho $MYDIR')
        temp_script.flush()
    
        my_dir = subprocess.check_output(["/bin/sh", temp_script.name])
        print 'my_dir =', my_dir
    

    这个 hack 涉及将 env_file 的内容复制到另一个临时文件中,然后在复制文件的末尾添加一个 echo 语句,执行它并收集输出。

    另外,我的假设是原始脚本不输出任何内容。如果是这样,您必须解析输出并找到您要查找的内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-05
      • 2017-10-26
      • 1970-01-01
      • 2017-02-03
      • 2017-09-08
      • 1970-01-01
      相关资源
      最近更新 更多