2009-07-06 77 views
16

如何持久地从Python脚本修改Windows环境变量? (这是setup.py脚本)从Python修改Windows环境变量的界面

我正在寻找一个标准函数或模块来使用它。我已经熟悉了registry way of doing it,但任何有关这方面的意见也是值得欢迎的。

+0

你的意思是持续性的环境变量(比如'setx'命令)还是改变当前进程的变量(比如'set'命令)?事实上,ActiveState上有一个配方表明没有标准模块,除非它是非常新的。 – 2009-07-06 07:57:41

+0

是的,持续地,我已经更新了这个问题 – sharkin 2009-07-06 08:29:09

+0

Duplicate:http://stackoverflow.com/questions/263005/is-it-possible-to-change-the-environment-of-a-parent-process-in- python – 2009-07-06 12:28:14

回答

20

使用SETX有一些缺点,特别是如果你想追加到环境变量(例如PATH SETX%PATH%; C:\ mypath中)这将每次运行它的时候一再追加到路径,它可以成为一个问题。更糟糕的是,它不区分机器路径(存储在HKEY_LOCAL_MACHINE中)和用户路径(存储在HKEY_CURRENT_USER中)。您在命令提示符下看到的环境变量由这两个值的串联组成。因此,呼吁SETX前:

user PATH == u 
machine PATH == m 
%PATH% == m;u 

> setx PATH %PATH%;new 

Calling setx sets the USER path by default, hence now: 
user PATH == m;u;new 
machine PATH == m 
%PATH% == m;m;u;new 

系统路径不可避免地在%PATH%环境变量每次调用SETX时间追加到PATH复制。这些更改是永久性的,不会因重新启动而重置,因此会在整个机器的使用期限内累积。

在DOS中试图弥补这一点是超出我的能力。所以我转向了Python。我想出了今天的解决方案,通过调整注册表,包括附加到路径,而不引入重复设置环境变量,如下:

from os import system, environ 
import win32con 
from win32gui import SendMessage 
from _winreg import (
    CloseKey, OpenKey, QueryValueEx, SetValueEx, 
    HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, 
    KEY_ALL_ACCESS, KEY_READ, REG_EXPAND_SZ, REG_SZ 
) 

def env_keys(user=True): 
    if user: 
     root = HKEY_CURRENT_USER 
     subkey = 'Environment' 
    else: 
     root = HKEY_LOCAL_MACHINE 
     subkey = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' 
    return root, subkey 


def get_env(name, user=True): 
    root, subkey = env_keys(user) 
    key = OpenKey(root, subkey, 0, KEY_READ) 
    try: 
     value, _ = QueryValueEx(key, name) 
    except WindowsError: 
     return '' 
    return value 


def set_env(name, value): 
    key = OpenKey(HKEY_CURRENT_USER, 'Environment', 0, KEY_ALL_ACCESS) 
    SetValueEx(key, name, 0, REG_EXPAND_SZ, value) 
    CloseKey(key) 
    SendMessage(
     win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment') 


def remove(paths, value): 
    while value in paths: 
     paths.remove(value) 


def unique(paths): 
    unique = [] 
    for value in paths: 
     if value not in unique: 
      unique.append(value) 
    return unique 


def prepend_env(name, values): 
    for value in values: 
     paths = get_env(name).split(';') 
     remove(paths, '') 
     paths = unique(paths) 
     remove(paths, value) 
     paths.insert(0, value) 
     set_env(name, ';'.join(paths)) 


def prepend_env_pathext(values): 
    prepend_env('PathExt_User', values) 
    pathext = ';'.join([ 
     get_env('PathExt_User'), 
     get_env('PathExt', user=False) 
    ]) 
    set_env('PathExt', pathext) 



set_env('Home', '%HomeDrive%%HomePath%') 
set_env('Docs', '%HomeDrive%%HomePath%\docs') 
set_env('Prompt', '$P$_$G$S') 

prepend_env('Path', [ 
    r'%SystemDrive%\cygwin\bin', # Add cygwin binaries to path 
    r'%HomeDrive%%HomePath%\bin', # shortcuts and 'pass-through' bat files 
    r'%HomeDrive%%HomePath%\docs\bin\mswin', # copies of standalone executables 
]) 

# allow running of these filetypes without having to type the extension 
prepend_env_pathext(['.lnk', '.exe.lnk', '.py']) 

它不影响当前进程或父进程,但它会影响运行后打开的所有cmd窗口,而不需要重新启动,并且可以安全地编辑和重新运行多次,而不会引入任何重复。

1

注册表的方式是,如果你想永久修改它的一切,我想这是你想要的,因为它在setup.py。

暂时只是你的过程,然后os.environ是诀窍。

1

在os模块中,有getenv和putenv函数。然而,似乎运行putenv不能正常工作,并且必须使用Windows注册表而不是

this discussion

4

这可能是一样容易使用外部Windows setx命令:

C:\>set NEWVAR 
Environment variable NEWVAR not defined 

C:\>python 
Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on 
win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import os 
>>> os.system('setx NEWVAR newvalue') 
0 
>>> os.getenv('NEWVAR') 
>>> ^Z 


C:\>set NEWVAR 
Environment variable NEWVAR not defined 

现在打开一个新的命令提示符:

C:\>set NEWVAR 
NEWVAR=newvalue 

正如你所看到的,既不setx套当前会话的变量,也不是父进程的变量(第一个命令提示符)。但它确实将该变量永久地设置在注册表中以用于将来的过程。

我认为根本没有办法改变父进程的环境(如果有的话,我很乐意听到它!)。

3

我试图通过程序来改变当前DOS会话的环境,这一定是千年前的事了。问题是:该程序在其自己的DOS shell中运行,因此它必须在其父环境中运行。它从DOS信息块开始,沿着内存控制块链遍历,以查找该父级环境的位置。一旦我发现如何做到这一点,我对操纵环境变量的需求就消失了。我给你下面的Turbo Pascal的代码,但我想有至少三种更好的方法来达到目的:

  1. 创建一个批处理文件:(一)调用Python脚本(或无论什么),它会生成一个包含相应SET命令的临时批处理文件; (b)调用临时批处理文件(SET命令在当前shell中执行);和(c)删除临时批处理文件。

  2. 创建一个Python脚本,将类似“VAR1 = val1 \ nVAR2 = val2 \ nVAR3 = val3 \ n”的内容写入标准输出。使用这种方式在批处理文件:

    for /f "delims=|" %%X in ('callYourPythonScript') do set %%X

    等瞧:变量VAR1,VAR2和VAR3都被赋予了值。

  3. 修改Windows注册表并按照Alexander Prokofyev所述的here所述广播设置更改。

而且这里去帕斯卡代码(你可能需要一个荷兰语字典和Pascal编程的书),只是报告的内存位置的程序。它仍然似乎在Windows XP下工作,不管它报告我们正在运行DOS 5.00。这只是第一个开始,为了操纵选定的环境,要做很多低级编程。而作为指针结构可能看起来是正确的,我不那么肯定,如果1994年的环境模型仍持有这些天...

program MCBKETEN; 
uses dos, HexConv; 

{----------------------------------------------------------------------------} 
{ Programma: MCBKETEN.EXE             } 
{ Broncode : MCBKETEN.PAS             } 
{ Doel  : Tocht langs de MCB's met rapportage       } 
{ Datum : 11 januari 1994            } 
{ Auteur : Meindert Meindertsma           } 
{ Versie : 1.00               } 
{----------------------------------------------------------------------------} 

type 
    MCB_Ptr  = ^MCB; 
{ MCB_PtrPtr = ^MCB_Ptr; vervallen wegens DOS 2.11 -- zie verderop } 
    MCB   = record 
        Signatuur : char; 
        Eigenaar  : word; 
        Paragrafen : word; 
        Gereserveerd : array[1..3] of byte; 
        Naam   : array[1..8] of char; 
       end; 
    BlokPtr  = ^BlokRec; 
    BlokRec  = record 
        Vorige  : BlokPtr; 
        DitSegment, 
        Paragrafen : word; 
        Signatuur : string[6]; 
        Eigenaar, 
        Omgeving  : word; 
        Functie  : String4; 
        Oorsprong, 
        Pijl   : char; 
        KorteNaam : string[8]; 
        LangeNaam : string; 
        Volgende  : BlokPtr; 
       end; 
    PSP_Ptr  = ^PSP; 
    PSP   = record 
        Vulsel1  : array[1..44] of byte; 
        Omgeving  : word; 
        Vulsel2  : array[47..256] of byte; 
       end; 

var 
    Zone     : string[5]; 
    ProgGevonden, 
    EindeKeten, 
    Dos3punt2    : boolean; 
    Regs     : registers; 
    ActMCB    : MCB_Ptr; 
    EersteSchakel, Schakel, 
    LaatsteSchakel  : BlokPtr; 
    ActPSP    : PSP_Ptr; 
    EersteProg, 
    Meester, Ouder, 
    TerugkeerSegment, 
    TerugkeerOffset, 
    TerugkeerSegment2, 
    OuderSegment   : word; 
    Specificatie   : string[8]; 
    ReleaseNummer   : string[2]; 
    i      : byte; 


{----------------------------------------------------------------------------} 
{ PROCEDURES EN FUNCTIES             } 
{----------------------------------------------------------------------------} 

function Coda (Omgeving : word; Paragrafen : word) : string; 

var 
    i   : longint; 
    Vorige, Deze : char; 
    Streng  : string; 

begin 
    i := 0; 
    Deze := #0; 
    repeat 
     Vorige := Deze; 
     Deze := char (ptr (Omgeving, i)^); 
     inc (i); 
    until ((Vorige = #0) and (Deze = #0)) or (i div $10 >= Paragrafen); 
    if (i + 3) div $10 < Paragrafen then begin 
     Vorige := char (ptr (Omgeving, i)^); 
     inc (i); 
     Deze := char (ptr (Omgeving, i)^); 
     inc (i); 
     if (Vorige = #01) and (Deze = #0) then begin 
     Streng := ''; 
     Deze := char (ptr (Omgeving, i)^); 
     inc (i); 
     while (Deze <> #0) and (i div $10 < Paragrafen) do begin 
      Streng := Streng + Deze; 
      Deze := char (ptr (Omgeving, i)^); 
      inc (i); 
     end; 
     Coda := Streng; 
     end 
     else Coda := ''; 
    end 
    else Coda := ''; 
end {Coda}; 


{----------------------------------------------------------------------------} 
{ HOOFDPROGRAMMA               } 
{----------------------------------------------------------------------------} 

BEGIN 
    {----- Initiatie -----} 
    Zone   := 'Lower'; 
    ProgGevonden := FALSE; 
    EindeKeten  := FALSE; 
    Dos3punt2  := (dosversion >= $1403) and (dosversion <= $1D03); 
    Meester   := $0000; 
    Ouder   := $0000; 
    Specificatie[0] := #8; 
    str (hi (dosversion) : 2, ReleaseNummer); 
    if ReleaseNummer[1] = ' ' then ReleaseNummer[1] := '0'; 

    {----- Pointer naar eerste MCB ophalen ------} 
    Regs.AH := $52; { functie $52 geeft adres van DOS Info Block in ES:BX } 
    msdos (Regs); 
{ ActMCB := MCB_PtrPtr (ptr (Regs.ES, Regs.BX - 4))^; NIET onder DOS 2.11 } 
    ActMCB := ptr (word (ptr (Regs.ES, Regs.BX - 2)^), $0000); 

    {----- MCB-keten doorlopen -----} 
    new (EersteSchakel); 
    EersteSchakel^.Vorige := nil; 
    Schakel    := EersteSchakel; 
    repeat 
     with Schakel^ do begin 
     DitSegment := seg (ActMCB^); 
     Paragrafen := ActMCB^.Paragrafen; 
     if DitSegment + Paragrafen >= $A000 then 
      Zone := 'Upper'; 
     Signatuur := Zone + ActMCB^.Signatuur; 
     Eigenaar := ActMCB^.Eigenaar; 
     ActPSP  := ptr (Eigenaar, 0); 
     if not ProgGevonden then EersteProg := DitSegment + 1; 
     if Eigenaar >= EersteProg 
      then Omgeving := ActPSP^.Omgeving 
      else Omgeving := 0; 
     if DitSegment + 1 = Eigenaar then begin 
      ProgGevonden := TRUE; 
      Functie  := 'Prog'; 
      KorteNaam[0] := #0; 
      while (ActMCB^.Naam[ ord (KorteNaam[0]) + 1 ] <> #0) and 
        (KorteNaam[0] < #8) do 
      begin 
       inc (KorteNaam[0]); 
       KorteNaam[ ord (KorteNaam[0]) ] := 
        ActMCB^.Naam[ ord (KorteNaam[0]) ]; 
      end; 
      if Eigenaar = prefixseg then begin 
       TerugkeerSegment := word (ptr (prefixseg, $000C)^); 
       TerugkeerOffset := word (ptr (prefixseg, $000A)^); 
       LangeNaam  := '-----> Terminate Vector = '  + 
            WordHex (TerugkeerSegment) + ':' + 
            WordHex (TerugkeerOffset)  ; 
      end 
      else 
       LangeNaam := ''; 
     end {if ÆProgØ} 
     else begin 
      if Eigenaar = $0008 then begin 
       if ActMCB^.Naam[1] = 'S' then 
        case ActMCB^.Naam[2] of 
        'D' : Functie := 'SysD'; 
        'C' : Functie := 'SysP'; 
        else Functie := 'Data'; 
        end {case} 
       else  Functie := 'Data'; 
       KorteNaam := ''; 
       LangeNaam := ''; 
      end {if Eigenaar = $0008} 
      else begin 
       if DitSegment + 1 = Omgeving then begin 
        Functie := 'Env '; 
        LangeNaam := Coda (Omgeving, Paragrafen); 
        if EersteProg = Eigenaar then Meester := Omgeving; 
       end {if ÆEnvØ} 
       else begin 
        move (ptr (DitSegment + 1, 0)^, Specificatie[1], 8); 
        if (Specificatie = 'PATH=' + #0 + 'CO') or 
        (Specificatie = 'COMSPEC='  ) or 
        (Specificatie = 'OS=DRDOS'  ) then 
        begin 
        Functie := 'Env' + chr (39); 
        LangeNaam := Coda (DitSegment + 1, Paragrafen); 
        if (EersteProg = Eigenaar) and 
         (Meester = $0000 ) 
        then 
         Meester := DitSegment + 1; 
        end 
        else begin 
        if Eigenaar = 0 
         then Functie := 'Free' 
         else Functie := 'Data'; 
        LangeNaam := ''; 
        if (EersteProg = Eigenaar) and 
         (Meester = $0000 ) 
        then 
         Meester := DitSegment + 1; 
        end; 
       end {else: not ÆEnvØ}; 
       KorteNaam := ''; 
      end {else: Eigenaar <> $0008}; 
     end {else: not ÆProgØ}; 

     {----- KorteNaam redigeren -----} 
     for i := 1 to length (KorteNaam) do 
      if KorteNaam[i] < #32 then KorteNaam[i] := '.'; 
     KorteNaam := KorteNaam + '  '; 

     {----- Oorsprong vaststellen -----} 
     if EersteProg = Eigenaar 
      then Oorsprong := '*' 
      else Oorsprong := ' '; 

     {----- Actueel proces (uitgaande Pijl) vaststellen -----} 
     if Eigenaar = prefixseg 
      then Pijl := '>' 
      else Pijl := ' '; 
     end {with Schakel^}; 

    {----- MCB-opeenvolging onderzoeken/schakelverloop vaststellen -----} 
     if (Zone = 'Upper') and (ActMCB^.Signatuur = 'Z') then begin 
     Schakel^.Volgende := nil; 
     EindeKeten  := TRUE; 
     end 
     else begin 
     ActMCB := ptr (seg (ActMCB^) + ActMCB^.Paragrafen + 1, 0); 
     if ((ActMCB^.Signatuur <> 'M') and (ActMCB^.Signatuur <> 'Z')) or 
      ($FFFF - ActMCB^.Paragrafen < seg (ActMCB^)    ) 
     then begin 
      Schakel^.Volgende := nil; 
      EindeKeten  := TRUE; 
     end 
     else begin 
      new (LaatsteSchakel); 
      Schakel^.Volgende  := LaatsteSchakel; 
      LaatsteSchakel^.Vorige := Schakel; 
      Schakel    := LaatsteSchakel; 
     end {else: (ÆMØ or ÆZØ) and Æteveel_ParagrafenØ}; 
     end {else: ÆLowerØ or not ÆZØ}; 
    until EindeKeten; 

    {----- Terugtocht -----} 
    while Schakel <> nil do with Schakel^ do begin 

    {----- Ouder-proces vaststellen -----} 
     TerugkeerSegment2 := TerugkeerSegment + (TerugkeerOffset div $10); 
     if (DitSegment    <= TerugkeerSegment2) and 
     (DitSegment + Paragrafen >= TerugkeerSegment2) 
     then 
     OuderSegment := Eigenaar; 

    {----- Meester-omgeving markeren -----} 
     if DitSegment + 1 = Meester then Oorsprong := 'M'; 

    {----- Schakel-verloop -----} 
     Schakel := Schakel^.Vorige; 
    end {while Schakel <> nil}; 

    {----- Rapportage -----} 
    writeln ('Chain of Memory Control Blocks in DOS version ', 
      lo (dosversion), '.', ReleaseNummer, ':'); 
    writeln; 
    writeln ('[email protected] #Par Signat [email protected] [email protected] Type !! Name  File'); 
    writeln ('---- ---- ------ ---- ---- ---- -- -------- ', 
      '-----------------------------------'); 
    Schakel := EersteSchakel; 
    while Schakel <> nil do with Schakel^ do begin 

    {----- Ouder-omgeving vaststellen -----} 
     if Eigenaar = OuderSegment then begin 
     if not Dos3punt2 then begin 
      if (Functie = 'Env ') then begin 
       Ouder := DitSegment + 1; 
       Pijl := 'Û'; 
      end 
      else 
       Pijl := '<'; 
     end {if not Dos3punt2} 
     else begin 
      if ((Functie = 'Env' + chr (39)) or (Functie = 'Data')) and 
       (Ouder = $0000) 
      then begin 
       Ouder := DitSegment + 1; 
       Pijl := 'Û'; 
      end 
      else 
       Pijl := '<'; 
     end {else: Dos3punt2}; 
     end {with Schakel^}; 

    {----- Keten-weergave -----} 
     writeln (WordHex (DitSegment)  , ' ', 
       WordHex (Paragrafen)  , ' ', 
       Signatuur     , ' ', 
       WordHex (Eigenaar)   , ' ', 
       WordHex (Omgeving)   , ' ', 
       Functie      , ' ', 
       Oorsprong, Pijl    , ' ', 
       KorteNaam     , ' ', 
       LangeNaam      ); 

    {----- Schakel-verloop -----} 
     Schakel := Schakel^.Volgende; 
    end {while Schakel <> nil}; 

    {----- Afsluiting rapportage -----} 
    writeln; 

    write ('* = First command interpreter at '); 
    if ProgGevonden 
     then writeln (WordHex (EersteProg), ':0000') 
     else writeln ('?'); 

    write ('M = Master environment  at '); 
    if Meester > $0000 
     then writeln (WordHex (Meester), ':0000') 
     else writeln ('?'); 

    write ('< = Parent proces    at '); 
    writeln (WordHex (OuderSegment), ':0000'); 

    write ('Û = Parent environment  at '); 
    if Ouder > $0000 
     then writeln (WordHex (Ouder), ':0000') 
     else writeln ('?'); 

    writeln ('> = Current proces   at ', 
      WordHex (prefixseg), ':0000'); 

    writeln (' returns     to ', 
      WordHex (TerugkeerSegment), ':', WordHex (TerugkeerOffset)); 
END. 

(以上ASCII 127,有可能在一些ASCII/ANSI翻译问题。此演示文稿)

1

这Python的脚本[*]试图修改注册表中的GLOBAL ENV-瓦尔,如果没有权限下降,回到用户的注册表,并通知有关更改的所有窗口:

""" 
Show/Modify/Append registry env-vars (ie `PATH`) and notify Windows-applications to pickup changes. 

First attempts to show/modify HKEY_LOCAL_MACHINE (all users), and 
if not accessible due to admin-rights missing, fails-back 
to HKEY_CURRENT_USER. 
Write and Delete operations do not proceed to user-tree if all-users succeed. 

Syntax: 
    {prog}     : Print all env-vars. 
    {prog} VARNAME   : Print value for VARNAME. 
    {prog} VARNAME VALUE : Set VALUE for VARNAME. 
    {prog} +VARNAME VALUE : Append VALUE in VARNAME delimeted with ';' (i.e. used for `PATH`). 
    {prog} -VARNAME  : Delete env-var value. 

Note that the current command-window will not be affected, 
changes would apply only for new command-windows. 
""" 

import winreg 
import os, sys, win32gui, win32con 

def reg_key(tree, path, varname): 
    return '%s\%s:%s' % (tree, path, varname) 

def reg_entry(tree, path, varname, value): 
    return '%s=%s' % (reg_key(tree, path, varname), value) 

def query_value(key, varname): 
    value, type_id = winreg.QueryValueEx(key, varname) 
    return value 

def show_all(tree, path, key): 
    i = 0 
    while True: 
     try: 
      n,v,t = winreg.EnumValue(key, i) 
      print(reg_entry(tree, path, n, v)) 
      i += 1 
     except OSError: 
      break ## Expected, this is how iteration ends. 

def notify_windows(action, tree, path, varname, value): 
    win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment') 
    print("---%s %s" % (action, reg_entry(tree, path, varname, value))) 

def manage_registry_env_vars(varname=None, value=None): 
    reg_keys = [ 
     ('HKEY_LOCAL_MACHINE', r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'), 
     ('HKEY_CURRENT_USER', r'Environment'), 
    ] 
    for (tree_name, path) in reg_keys: 
     tree = eval('winreg.%s'%tree_name) 
     try: 
      with winreg.ConnectRegistry(None, tree) as reg: 
       with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key: 
        if not varname: 
         show_all(tree_name, path, key) 
        else: 
         if not value: 
          if varname.startswith('-'): 
           varname = varname[1:] 
           value = query_value(key, varname) 
           winreg.DeleteValue(key, varname) 
           notify_windows("Deleted", tree_name, path, varname, value) 
           break ## Don't propagate into user-tree. 
          else: 
           value = query_value(key, varname) 
           print(reg_entry(tree_name, path, varname, value)) 
         else: 
          if varname.startswith('+'): 
           varname = varname[1:] 
           value = query_value(key, varname) + ';' + value 
          winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, value) 
          notify_windows("Updated", tree_name, path, varname, value) 
          break ## Don't propagate into user-tree. 
     except PermissionError as ex: 
      print("!!!Cannot access %s due to: %s" % 
        (reg_key(tree_name, path, varname), ex)) 
     except FileNotFoundError as ex: 
      print("!!!Cannot find %s due to: %s" % 
        (reg_key(tree_name, path, varname), ex)) 

if __name__=='__main__': 
    args = sys.argv 
    argc = len(args) 
    if argc > 3: 
     print(__doc__.format(prog=args[0])) 
     sys.exit() 

    manage_registry_env_vars(*args[1:]) 

下面是一些使用示例,假设它已被保存n当前路径中的某个文件名为setenv.py。 注意,在这些例子我没有管理员权限,这样的变化只影响到我的本地用户的注册表树:

> REM ## Print all env-vars 
> setenv.py 
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied 
HKEY_CURRENT_USER\Environment:PATH=... 
... 

> REM ## Query env-var: 
> setenv.py PATH C:\foo 
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied 
!!!Cannot find HKEY_CURRENT_USER\Environment:PATH due to: [WinError 2] The system cannot find the file specified 

> REM ## Set env-var: 
> setenv.py PATH C:\foo 
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied 
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo 

> REM ## Append env-var: 
> setenv.py +PATH D:\Bar 
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied 
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo;D:\Bar 

> REM ## Delete env-var: 
> setenv.py -PATH 
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:PATH due to: [WinError 5] Access is denied 
---Deleted HKEY_CURRENT_USER\Environment:PATH 

[*]摘自:http://code.activestate.com/recipes/416087-persistent-environment-variables-on-windows/