如何在Windows命令行中永久更新PATH变量?

129

如果我在命令行(cmd.exe)中执行set PATH=%PATH%;C:\\Something\\bin,然后执行echo %PATH%,我会看到将该字符串添加到PATH中。如果我关闭并重新打开命令行,则该新字符串不在PATH中。

如何永久地从命令行更新所有进程的PATH,而不仅仅是当前进程?

我不想通过转到“系统属性”→“高级”→“环境变量”并在那里更新PATH来实现此目的。

这个命令必须从Java应用程序中执行(请参见我的其他问题)。


5
使用PowerShell相对来说比较简单,可以参考该网址https://dev59.com/7XRB5IYBdhLWcg3wHkPi。使用cmd命令行则不确定该怎么做,可能需要修改注册表或以某种方式引入.net程序集。 - Austen Holmes
1
就像我之前说的,我必须在Java应用程序内完成这个任务。我想通过使用Java的Runtime.getRuntime().exec("我的命令");来执行一些CMD命令。 - vale4674
这回答解决了您的问题吗?如何将一个目录添加到 Windows 的 PATH 环境变量中 - Dave Jarvis
7个回答

144

您可以使用:

setx PATH "%PATH%;C:\\Something\\bin"

然而,setx命令会截断存储的字符串到1024字节,可能会破坏路径。

/M选项将在HKEY_LOCAL_MACHINE而不是HKEY_CURRENT_USER中更改PATH。换句话说,它更改的是系统变量而不是用户的变量。例如:

SETX /M PATH "%PATH%;C:\your path with spaces"

请注意,新的 PATH 在您当前的 cmd.exe 中是不可见的。

但是,如果您在注册表或者新的 cmd.exe 中使用 "set p" 命令,您可以看到新的值。


2
有没有办法使用 setx 更改机器的路径而不是用户的路径? - Corey Ogburn
4
这里可以看出,在Windows XP和7上使用命令末尾的/m,可能不仅可以为当前登录的用户设置变量,还可以为整个计算机设置。但我没有试过。 - panny
1
当运行setx命令时,我收到了错误提示:“默认选项不允许超过'2'次”。如何绕过这个问题? - Nam G VU
13
@KilgoreCod评论说:我警告不要使用该命令:在许多(大多数?)现代安装中,PATH变量会很长 - setx将把存储的字符串截断为1024字节,可能会破坏PATH(请参见这里的讨论http://superuser.com/q/812754)。 - beresfordt
2
我试图回显路径,但已经超过了1200字节。除了setx之外还有其他方法吗? - Laurence
显示剩余9条评论

43

如何执行此操作的文档可以在MSDN上找到。关键信息如下:

要以编程方式添加或修改系统环境变量,请将其添加到HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment注册表键中,然后广播带有lParam设置为字符串“Environment”的WM_SETTINGCHANGE消息。这使得应用程序(例如shell)可以获取您的更新。

请注意,您的应用程序需要具有提升的管理员权限才能修改此键。

您在评论中表示,您很乐意仅修改每个用户的环境。通过编辑HKEY_CURRENT_USER\Environment中的值来实现此目的。与之前一样,请确保广播WM_SETTINGCHANGE消息。

您应该能够使用JNI注册表类轻松地从Java应用程序中执行此操作。


1
是的,可以使用JNI注册表类。更大的问题是您的应用程序可能没有以提升的权限运行。您知道如何使它这样做吗?如果您只想使应用程序的一小部分以提升的权限运行(即仅执行此更改),则最简单的解决方案是编写一个非常简单的C++应用程序来完成该任务,并标记应用程序清单,然后作为单独的进程执行,引发UAC对话框。 - David Heffernan
1
您还可以编辑HKEY_CURRENT_USER\Environment以避免提升要求。 - kichik
是的,就像David所说的那样。只是你不需要提高权限。我还应该提到,这将仅修改当前用户的环境。 - kichik
@David Heffernan,我现在很困惑,我是否需要那个C++程序?Kichink让我感到困惑,他说可以从Java应用程序中完成。我不确定他是否也认为我需要C++程序。 - vale4674
如果我是你,我会在应用程序的安装程序中处理所有这些内容。每个体面的安装框架都可以更改环境。 - kichik
显示剩余8条评论

39

我建议不要使用这个命令。

setx PATH "%PATH%;C:\Something\bin"

由于其实现的“特性”,需要修改PATH变量。在许多(大多数?)安装中,该变量将非常长 - setx将截断存储的字符串为1024个字节,可能会破坏PATH(请参见此处的讨论here)。

我专门注册以标记此问题,因此缺乏站点声誉,无法直接评论2012年5月2日发布的答案。谢谢beresfordt添加这样的评论


9
这个Python脚本[*]正是做到了这一点:
"""
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 yield_all_entries(tree, path, key):
    i = 0
    while True:
        try:
            n,v,t = winreg.EnumValue(key, i)
            yield 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)), file=sys.stderr)

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:
                        for regent in yield_all_entries(tree_name, path, key):
                            print(regent)
                    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), file=sys.stderr)
        except FileNotFoundError as ex:
            print("!!!Cannot find %s due to: %s" % 
                    (reg_key(tree_name, path, varname), ex), file=sys.stderr)

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

    manage_registry_env_vars(*args[1:])

以下是一些用法示例,假设已将其保存在名为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/ 本文介绍如何在Windows上设置持久环境变量。

4
在企业网络中,用户仅具有有限的访问权限并使用便携式应用程序时,有以下命令行技巧:
  1. 查询用户环境变量:reg query "HKEY_CURRENT_USER\Environment"。对LOCAL_MACHINE使用"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
  2. 添加新的用户环境变量:reg add "HKEY_CURRENT_USER\Environment" /v shared_dir /d "c:\shared" /t REG_SZ。对于包含其他%%变量的路径,请使用REG_EXPAND_SZ
  3. 删除现有的环境变量:reg delete "HKEY_CURRENT_USER\Environment" /v shared_dir

4

为了参考,对于任何想要通过代码更改路径的人,我引用了一位Delphi程序员在此网页上发布的有用帖子:http://www.tek-tips.com/viewthread.cfm?qid=686382

TonHu (Programmer) 22 Oct 03 17:57 I found where I read the original posting, it's here: http://news.jrsoftware.org/news/innosetup.isx/msg02129....

The excerpt of what you would need is this:

You must specify the string "Environment" in LParam. In Delphi you'd do it this way:

 SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment')));

It was suggested by Jordan Russell, http://www.jrsoftware.org, the author of (a.o.) InnoSetup, ("Inno Setup is a free installer for Windows programs. First introduced in 1997, Inno Setup today rivals and even surpasses many commercial installers in feature set and stability.") (I just would like more people to use InnoSetup )

HTH


你需要修改注册表。此外,将整数强制转换为LPARAM而不是Integer可以提高64位兼容性。 - David Heffernan
这是一个例子:http://github.com/gilligan/snesdev/blob/1253994/tools/cc65-2.13.2/packages/windows/wm_envchange.c - user6307369

3

很棒的脚本。我使用HotKey,但不知道如何或需要做什么才能将脚本添加到其中。您能提供帮助、链接或解释需要做什么吗? - jwzumwalt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接