如何使用setx将内容添加到Windows PATH变量中?出现了奇怪的问题。

110

我想使用setx修改Windows的PATH变量。以下代码在Windows 8系统中至少50%的时间可以实现:

setx PATH %PATH%;C:\Python27\;C:\Python27\Scripts\

如果出现“默认参数只能用2次”的错误,则以下方法有时有效:

setx PATH "%PATH%;C:\Python27\;C:\Python27\Scripts\"

不同之处在于我们用引号包裹了第二个参数。我认为当%PATH%包含空格时,引号是必需的。

然而,在Windows 7上,我遇到了一些奇怪的问题。在一个特定的Windows 7机器上,我遇到了这个问题:

echo %PATH%

它会打印:

C:\Foo\;C:\Bar\;[...lots of stuff...]C:\Baz\

然后我做这个:

setx PATH "%PATH%;C:\Quux\"

接着它会显示“错误:截断于1,024个字符。”现在让我们检查一下PATH包含什么内容:

echo %PATH%

它会输出:

C:\Foo\;C:\Foo\;C:\Bar\;C:\Bar\;[...lots of stuff, now duplicated...]C:\B

…并且它被截断在1,024个字符处。由于重复项的存在,它超过了这个限制。还有一件有趣的事情:尽管setx引发了错误并且没有显示“成功”,但PATH的值仍然发生了变化。

我能够多次重现这种奇怪的行为(幸运的是我保存了PATH的原始内容)。

目前,我知道唯一可靠的追加PATH的方法如下:

  1. echo PATH。

  2. 将PATH的内容复制到文本文件中,并手动在末尾添加 ;C:\Python27\;C:\Python27\Scripts\

  3. 将整个内容从文本文件复制出来。

  4. setx PATH "<粘贴字符串>"

这个过程在Windows 7和Windows 8上每次都有效。

我真的应该能够用一条命令完成这个操作。我做错了什么?


12
使用setx的另一个危险之处在于:如果路径包含任何环境变量,例如%JAVADIR%\bin,则引用将丢失,即如果JAVADIR更改,则路径将不再随之更改。如果这是软件安装程序,可能会破坏最终用户的计算机。不是一个好主意。 - Harry Johnston
1
@HarryJohnston,这是设计上的问题。每当你使用%时,它会立即进行解释。 - Pacerier
2
@Pacerier,路径的注册表条目中的任何环境变量引用都已经被展开,这是在构建环境块时发生的。因此,当您将%PATH%输入到setx中时,您会丢失此信息。另外,当然,OP正在做的方式是将系统路径设置添加到用户路径设置中,再次查看问题,看起来这才是OP问题的真正原因。 - Harry Johnston
3
有四种环境变量类型:hkcu reg_sz、hkcu reg_expand_sz、hklm reg_sz 和 hklm reg_expand_sz。(可以使用regedit手动调整它们并运行setx来完成刷新。)如果您的输入包含2个以上的“%”字符,则setxsetx / m默认为reg_sz类型。无论是在hklm还是hkcu中,"expand"都意味着将其扩展到hklm。以任何顺序运行setx/m qwe %JAVADIR%\binsetx/m javadir asdfg,然后打开新的命令提示符窗口并执行echo %qwe%:结果符合预期。 - Pacerier
3
@Pacerier,我不认为这与问题相关。PATH的值已经扩展并且包含HKCU和HKLM条目(后者是一种特殊情况,对于任何其他环境变量都不会发生),所以如果HKLM中的路径最初包含%JAVADIR%\bin,而HKCU中的路径最初包含%JDK%\bin,那么PATH将被设置为C:\Java\bin;C:\jdk\bin,所以当您使用setx PATH %PATH%时,HKCU中的值将更改为C:\Java\bin;C:\jdk\bin,其中(a)包含多余的条目,(b)不再跟踪JDK值的更改。 - Harry Johnston
显示剩余6条评论
12个回答

95

以管理员身份运行cmd,然后执行以下操作:

Run cmd as administrator, then:

setx /M PATH "%PATH%;<your-new-path>"

/M选项将变量设置为系统范围。默认行为是为用户设置它。

简要概述:

截断发生的原因是当你回显%PATH%时,你会得到SYSTEM和USER值的串联,因此当你在第二个参数中添加它到setx时,该命令将尝试将SYSTEM和USER的内容都适合于USER变量中。当你再次回显时,结果将加倍。

/M选项需要管理员特权,因此你需要以“以管理员身份运行”的方式打开终端,否则setx将失败并提示“拒绝访问注册表路径”。

注意: 当你以这种方式设置新值后,在回显%PATH%时你看不到新值,你需要关闭cmd并重新打开。

如果你想检查存储在注册表中的实际值,请查看此问题


41
这个解决方案是错误的。执行上述命令将会把您 Path 的类型从 REG_EXPAND_SZ 改为 REG_SZ,并在此过程中删除所有存在的环境变量引用。 - user2023370
感谢您的反馈,@garyM。在我的情况下,我没有经历/注意到所提到的后果。如果有修改此答案的方法,请告诉我们。或者,如果您建议另一个答案,请告诉我们。 - Victor Basso
2
嗨Vituel,问题出现在变量新创建后在同一进程中被修改。新创建的变量不会显示在现有的shell环境中。任何引用都将把引用%var%视为文本字符串。为避免此问题,必须在创建变量之后执行访问变量的新进程中执行。在某些Windows版本中,路径变量是一个特殊情况。在2008R2中,修改PATH变量不会推广到现有进程,它们需要重新实例化以获取更改。根据操作系统,/M位于末尾。 - garyM
3
这会导致将用户的PATH变量追加到机器的PATH变量中,这样会(a)干扰其他用户,(b)如果重复运行,路径长度将不断增加,直到最终溢出。大多数情况下,不需要为PATH设置用户设置,因此您可能可以通过,但这是有风险的。 - Harry Johnston
10
不要使用setx命令,它会将PATH路径缩短为1024个字符。如果之前通过在路径前面添加更多目录来更改了PATH路径,那么你将会失去大部分原始的PATH内容! - e.tadeu

37

PATH环境变量由存储在注册表中的系统级部分和用户特定部分构成。

以下命令确保不会干扰PATH的系统级部分。仅读取、修改和写回注册表中的用户特定部分。

for /f "usebackq tokens=2,*" %A in (`reg query HKCU\Environment /v PATH`) do set my_user_path=%B
setx PATH "C:\Python27;C:\Python27\Scripts;%my_user_path%"
第一条命令从注册表获取用户特定的PATH部分,并将其存储在名为my_user_path的变量中。
注意:确保第一条命令能够无错误运行。
第二条命令将C:\Python27;C:\Python27\Scripts;添加到my_user_path的值之前,并将其写回到用户特定的PATH注册表键中。
注意:在.bat文件中使用时,请在第一行的两个位置上用%%替换%
for /f "usebackq tokens=2,*" %%A in (`reg query HKCU\Environment /v PATH`) do set my_user_path=%%B

只有新启动的程序才会看到PATH环境变量的更改。


2
这个答案在我的逻辑看来是正确的。为什么它既没有得到更多的投票,也没有被接受呢?我有什么遗漏吗? - Siva Sankaran
3
@AMVaddictionist - 我认为 %% 或 % 与您是将其作为 .bat 文件运行还是直接在 cmd 中运行有关。希望这可以帮到您。 - Manohar Reddy Poreddy
3
同意,这是正确答案。按原样编写的话,它适用于在命令提示符中运行,并且单个“%”必须替换为双重“%%”以在批处理文件中运行。 - JonathanDavidArndt
2
@ManoharReddyPoreddy 感谢您的回答,感谢上帝拯救了我。虽然我备份了环境变量,但这个答案比其他任何答案都珍贵。首先存储先前的环境变量,然后附加新的环境变量。非常有效 :) - mozilla-firefox
1
@mozilla_firefox - 是啊,我们备份后再尝试新的东西 :), 我们是合格的开发者? :D - Manohar Reddy Poreddy
显示剩余7条评论

8

如果有人想在PowerShell中运行它,可以按照以下步骤进行操作:

以管理员身份运行PowerShell

然后执行以下命令:

setx /M PATH "$Env:PATH;<path to add>"

为了验证,请打开另一个 Powershell 并查看 PATH,如下所示:

$Env:PATH

4

1
如果您想让路径的一部分更改为%USERPROFILE%,则可以在批处理文件中使用双%%(或者如果直接运行命令,则使用^% - 参见如何在bat vs cmd中转义)。 - TamaMcGlinn

4

6
谢谢您的建议,但我真的不想依赖第三方软件。我只想使用Windows自带的东西。原因是我有两个主要用例:(1)设置想学习Python的新手,以及(2)设置在我们公司工厂中使用的机器。在这两种情况下,都是一个随机的Windows机器,只需要编辑一次路径。我希望尽可能简化路径编辑,并且安装更多的软件真的不行。这就是为什么即使我的四步解决方法也还不够好的原因。 - SerMetAla
所以我提供的工具有便携式二进制文件,您可以将其捆绑在zip文件中。您可以让用户运行一个简单的.bat文件,让它处理PATH编辑以及设置Python代码。 - Ehtesh Choudhury

3
很遗憾,使用OOTB工具,您无法直接/轻松地附加系统路径或用户路径。如果您想坚持使用OOTB工具,您必须查询SYSTEM或USER路径,将该值保存为变量,然后附加您的添加内容并使用setx保存它。下面的两个示例显示了如何检索和保存这两者,并附加您的添加内容。不要混淆%PATH%,它是USER+SYSTEM的连接,并且会导致结果中出现很多重复。您必须像下面所示拆分它们...
附加到系统路径
for /f "usebackq tokens=2,*" %A in (`reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH`) do set SYSPATH=%B

setx PATH "%SYSPATH%;C:\path1;C:\path2" /M

追加到用户路径中

for /f "usebackq tokens=2,*" %A in (`reg query HKCU\Environment /v PATH`) do set userPATH=%B

setx PATH "%userPATH%;C:\path3;C:\path4"

这里8年来唯一明智的答案。除了REG_EXPAND_SZREG_SZ问题和1024个字符限制可能仍然存在问题。还要查看setx /?,因为它可以直接读取注册表。 - Amit Naidu
唯一使用FOR循环包装它的原因是将第一个命令的结果放入变量中。完全反向的DOS脚本,但无法避免。不知道setx可以读取这些内容,所以感谢这个补充。 - Inetquestion

2
我曾经遇到同样的问题,现在找到了一个简单的解决方案。
使用 pathman 工具。
pathman /as %M2%

例如将%M2%添加到系统路径中。没有多余的,也没有不足的。不再出现获取用户路径和系统路径混合的问题。不再费力从注册表中获取正确的值...

在Windows 10上进行了测试


pathman 完美地完成了工作。应向后兼容 Windows 2003 资源工具包 - Pat Migliaccio
@PatMigliaccio,你的链接已经失效了。 - TamaMcGlinn

2

我想再强调一下:来自Manohar Reddy Poreddy早期答案是正确的。

有许多可以改进的地方,我认为分享这些可能会有所帮助:

@echo off

REM -- Use directory where script is located. For current working directory, use %CD%
set CURRENT_DIR=%~dp0
REM -- Great example from Strawberry Perl's portable shell launcher:
if #%CURRENT_DIR:~-1%# == #\# set CURRENT_DIR=%CURRENT_DIR:~0,-1%

REM -- Load the user PATH from the registry, making sure it is not blank
reg query HKCU\Environment /v PATH 1>nul 2>&1
if ERRORLEVEL 1 goto skipLoadPath
for /f "usebackq tokens=2,*" %%A in (`reg query HKCU\Environment /v PATH`) do set MY_USER_PATH=%%B

REM -- Optionally, checks if this directory is already part of the user PATH
REM -- This will avoid duplication, and allow this to be run multiple times,
REM -- but if a sub-directory of the current is already part of the user PATH,
REM -- this script will simply not do anything
echo %MY_USER_PATH%|find "%CURRENT_DIR%" >nul
if errorlevel 1 (goto skipLoadPath) else (goto skipSetXPath)
:skipLoadPath

REM -- Avoid adding a useless semicolon to the previous user PATH
if not "" == "%MY_USER_PATH%" set "MY_USER_PATH=%MY_USER_PATH%;"
REM -- Permanently set the following location as part of the system path
setx PATH "%MY_USER_PATH%%CURRENT_DIR%" >nul 2>&1
REM -- This will update the current environment, since SETX only updates the registry
set "PATH=%PATH%;%MY_USER_PATH%%CURRENT_DIR%"
:skipSetXPath

在查找子字符串的部分,我要感谢Magoo提供的这个答案 - JonathanDavidArndt
1
在我看来,这应该是被接受的答案,因为它避免了重复路径。小建议:考虑使用%1而不是硬编码的“当前目录”或任何显式策略。留给用户自由选择。 - kakyo
但是互联网上的人们不能简单地通过脚本小子来解决这个问题,他们可能真的需要思考如何使用它。 - JonathanDavidArndt
嗯,我知道你是双击它。不是为了挑剔,只是为了我的自用把它转换成了一个子程序。无论如何,还是谢谢你。 - kakyo
很棒。这是一个好的建议,是一种改进,我很感激。我跟任何人一样都不喜欢硬编码,但在这种情况下,我认为更好的方法是专注于问题的核心(重复路径等),而不是添加可能会或可能不会给出中心问题的清晰度的漂亮外设。 - JonathanDavidArndt

2
setx path "%PATH%; C:\Program Files (x86)\Microsoft Office\root\Office16" /m

这应该可以将内容追加到系统环境变量路径中,而不添加任何额外的内容,并保持原始数据完整无损。我使用了这个命令来解决 McAfee 的 Web Control 对 Microsoft Outlook 桌面客户端造成的问题。
路径值中使用引号是因为命令行将空格视为分隔符,并尝试在命令行中执行下一个值。引号覆盖了这种行为,并将引号内的所有内容作为字符串处理。

0
这个vbscript/batch混合体"append_sys_path.vbs"并不直观,但是它完美地工作:
If CreateObject("WScript.Shell").Run("%ComSpec% /C ""NET FILE""", 0, True) <> 0 Then
    CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """", , "runas", 5
    WScript.Quit
End If
Set Shell = CreateObject("WScript.Shell")
Cmd = Shell.Exec("%ComSpec% /C ""REG QUERY ""HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"" /v Path | FINDSTR /I /C:""REG_SZ"" /C:""REG_EXPAND_SZ""""").StdOut.ReadAll
Cmd = """" & Trim(Replace(Mid(Cmd, InStr(1, Cmd, "_SZ", VBTextCompare) + 3), vbCrLf, ""))
If Right(Cmd, 1) <> ";" Then Cmd = Cmd & ";"
Cmd = "%ComSpec% /C ""REG ADD ""HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"" /v Path /t REG_EXPAND_SZ /d " & Replace(Cmd & "%SystemDrive%\Python27;%SystemDrive%\Python27\Scripts"" /f""", "%", """%""")
Shell.Run Cmd, 0, True

这种方法的优点:

1)它不会将系统路径环境截断为1024个字符。
2)它不会连接系统和用户路径环境。
3)它会自动以管理员身份运行。
4)保留系统路径环境中的百分比符号。
5)支持空格、括号和特殊字符。
6)适用于Windows 7及以上版本。


请在代码中添加一些注释,以解释路径字符串的操作并消除换行符。为什么需要 findstr 命令?/v path 查询是否会返回多行?不要将保存路径值的变量命名为 'cmd',这会使本来就难以阅读的代码更加晦涩。对于那些不需要(或没有权限)修改系统路径的人来说,另一个简化版本的 HKCU 也会很有用。 - Amit Naidu
好的,现在我明白那两个cmd命令只是为了删除多余的findstr输出。这可以通过一个单独的命令更简单地完成:for /F "skip=2 tokens=2*" %a in ('reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v path') do @set currpath=%b。或者更好的方法是不使用cmd。当在vbscript中时,请使用vbscript。Shell.RegRead("HKLM\SOFTWARE\etc")。还可以参见RegWrite - Amit Naidu

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