启动新的cmd.exe而不继承环境变量?

34
如果我从一个已有的shell中启动一个新的CMD shell,新的shell会继承现有环境。有没有一种方法可以启动一个新的shell,但让它初始化为系统默认值而不是继承现有环境?
B:\>set _test=blooharky

B:\>cmd
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

B:\>set _
_test=blooharky

期望的结果:

B:\>set _test=blooharky

B:\>cmd /env=default
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

B:\>set _
Environment variable _ not defined

[更新] 此问题的解决方法是 start /i cmd,由 dbenham 在下面的链接中分享。然而,在当前 shell 已经是第二代的情况下,它并不能提供帮助。例如:

d:\>set _
Environment variable _ not defined

d:\>set _test=blooharky

d:\>cmd /k

:: some work done using _test here...
:: ...but after we need a new clean shell:

d:\>start /i cmd

d:\>set _
_test=blooharky

:: uhoh, our shell isn't clean!

1
这个问题有关闭的投票,我很好奇是为什么。它有什么让人不感兴趣或者不值得注意的地方吗? - matt wilkie
2
投票关闭你的问题的用户仅仅是建议它不适合在这里讨论,而应该在http://superuser.com/上讨论。 - Andriy M
是的,那是我,我认为在超级用户上会更好。不过显然我的意见被否决了! :) - Cam Jackson
6个回答

19

Anders的方法对于创建新环境有非常详细的控制,我选择接受他的答案是基于这个深度掌控。然而,在那个回答中的一个短语引导我找到了解决促使我发起这个问题的具体问题的方法。它是一个"与初始explorer.exe环境相等的环境"。哦!我知道如何做到这点

start /i "%windir%\explorer.exe" "%windir%\system32\cmd.exe"
注意:最初这只是 start /i explorer ...,但事实证明不太可靠,应该为两个部分都使用完整路径。如果您只需要解决方案,现在您已经拥有一切,可以忽略其余内容。
根据Ander在下面的评论中提到的安全警告,我在4台Win7计算机上进行了测试,其中包括Pro、Home和Enterprise,32位和64位。我改变了用户帐户控制,全部开启或关闭。还使用非管理员命令提示符shell进行了测试。所有这些都没有出错或警告。
在作为本地管理员登录的Server 2003上,我会弹出一个窗口对话框“文件下载-安全警告:您要运行还是保存此文件?”它有运行、保存和取消按钮。按[运行]会导致另一个对话框,“Windows Explorer-安全警告:发布者无法验证。是否确定要运行此软件?”。再次按[运行]最终会得到一个可用的命令提示符。显然有一些奇怪的逻辑会使Internet Explorer替代Windows Explorer而没有exe的完整路径。
然而,情况变得更加奇怪:使用带有标题的start(一些人建议始终包括一个title),无论是否使用完整路径,都会导致安全警告出现。其他参数,如启动目录,似乎都可以正常工作。
:: security warning
start "clean shell" /i "%windir%\explorer.exe" "%windir%\system32\cmd.exe"

:: is okay
start /i  /d x:\ "%windir%\explorer.exe" "%windir%\system32\cmd.exe"

1
据我所知,这不是支持的浏览器语法,而且当我尝试时,会出现安全警告。 - Anders
这个解决方案真的需要使用 /I 选项吗?我认为 explorer.exe 会以任何方式启动默认值。 - dbenham
我只在Windows 10上进行了测试,但似乎只需要直接执行"%windir%\explorer.exe" "%windir%\system32\cmd.exe"(不需要整个/start开关)就足够了。 - undefined

6
如果你愿意使用PowerShell,你可以使用以下命令: Start-Process -UseNewEnvironment powershell 或者 Start-Process -UseNewEnvironment cmd
这些命令可用于IT技术相关内容。

6

一些变量在登录时被初始化,不会与其他注册表项一起存储,因此如果您想要一个与初始explorer.exe环境相等的环境,则需要将这些项目添加到白名单,并希望它们尚未被任何人更改:

@echo off
setlocal ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION
goto main

:SetFromReg
FOR /F "tokens=2,*" %%A IN ('REG query "%~1" /v "%~2"^|find /I "REG_"') DO (
    call set %~3=%%B
)
goto :EOF

:GetRegEnv
FOR /F %%A IN ('REG query "%~1" /s^|find /I "REG_"') DO (
    if /I not "%%~A"=="Path" call :SetFromReg "%~1" "%%~A" "%%~A"
)
goto :EOF

:InheritOrDelete
for %%A in (save_TEMP Path SystemRoot SystemDrive ProgramFiles CommonProgramFiles ALLUSERSPROFILE COMPUTERNAME LOGONSERVER USERNAME USERDOMAIN HOMEDRIVE HOMEPATH USERPROFILE APPDATA) do if /I "%%~A"=="%~1" goto :EOF
set %~1=
goto :EOF

:main
REM Save temp
set save_TEMP=%temp%
if not defined save_TEMP set save_TEMP=%tmp%

for /F "delims==" %%A in ('set') do call :InheritOrDelete "%%~A"
call :GetRegEnv "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
call :GetRegEnv "HKCU\Environment"

REM Special handling for Path
call :SetFromReg "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" Path Path
setlocal
set u=
call :SetFromReg "HKCU\Environment" Path u
endlocal&if not "%Path%"=="" if not "%u%"=="" set Path=%Path%;%u%

REM Restore TEMP/TMP
set TEMP=%save_TEMP%
set save_TEMP=
set TMP=%TEMP%

REM start some command...
start cmd /d /k set

从reg.exe输出的结果在每个Windows版本上都不相同,因此您需要确保它在目标系统上运行正常(如果变量名称包含空格,则会出现问题,您可以通过将“tokens = 2,*”替换为“tokens = 2,* delims =”(delims等于制表符)来解决这个问题,但在执行此操作之前,请确保reg.exe输出始终使用制表符作为分隔符)。
您可以通过使用Windows脚本主机脚本而不是批处理文件来解决这些问题:
'orgenvshell.vbs:
Set WShl = CreateObject( "WScript.Shell" )
Set FSO = CreateObject("Scripting.FileSystemObject")

Function CanInherit(n)
    CanInherit = False
    w = Split("SystemRoot SystemDrive ProgramFiles CommonProgramFiles ALLUSERSPROFILE COMPUTERNAME LOGONSERVER USERNAME USERDOMAIN HOMEDRIVE HOMEPATH USERPROFILE APPDATA")
    For Each i In w
        If 0 = StrComp(i,n,1) Then
            CanInherit = True
            Exit Function
        End If
    Next
End Function

Function GetShortFolderPath(p)
    GetShortFolderPath = p
    On Error Resume Next
    GetShortFolderPath = FSO.GetFolder(p).ShortPath
End Function

Sub E(dst,src)
    set envs = WShl.Environment(src)
    For Each i In envs
        t = Split(i,"=")
        n = t(0)
        If n = "" Then n = "="&t(1)
        If IsNull(dst) Then
            If not CanInherit(n) Then envs.Remove n
        Else
            v = Mid(i,Len(n)+2)
            envd = dst
            If "X" = dst Then
                v = WShl.ExpandEnvironmentStrings(v)
                envd = src
                If 0 = StrComp(n,"TMP",1) Then v = GetShortFolderPath(v)
                If 0 = StrComp(n,"TEMP",1) Then v = GetShortFolderPath(v)
            End If
            WShl.Environment(envd)(n) = v
        End If
    Next
End Sub

E Null,"PROCESS"
E "PROCESS","SYSTEM"
E "PROCESS","USER"
E "X","PROCESS"

'Special hack for Path
s = WShl.Environment("SYSTEM")("Path")
u = WShl.Environment("USER")("Path")
If Len(u) Then s = s&";"&u
WShl.Environment("PROCESS")("Path") = WShl.ExpandEnvironmentStrings(s)

'Test a command
WShl.Run "cmd /d /k set ",1

您可以通过查询WMI并使用其他WSH方法来删除许多白名单项...


1
为什么不直接执行 start/i %windir%/explorer "%windir%\system32\cmd.exe" 呢? - Pacerier
@Pacerier 从技术上讲,即使它似乎起作用,也不是有效的 Explorer 语法。请参阅下面的答案以了解如何解决安全警告问题。 - Anders

5

我不知道在同一窗口中如何实现这一点。但是您可以在单独的窗口中使用以下方式完成任务

start /i cmd

如果您希望当前的cmd会话等待新会话结束,可以添加/wait选项。 编辑: 根据Romário的评论,我做出了更正。您可以添加/B和/WAIT选项在同一个窗口中创建新的cmd.exe会话。
start /i /b /wait cmd

请注意,新的cmd.exe继承了父进程启动时存在的初始环境。如果父进程本身是从另一个cmd.exe环境启动的,则可能不是默认环境。


好的,这对我来说是一个部分解决方案,新窗口并不是致命问题。但它揭示了我的初始问题没有包含足够的信息。我需要启动一个干净的系统默认环境,我的父shell不是系统默认的,并且有一些设置被继承到start /i cmd shell中,这些设置不应该存在。我会相应地更新问题。 - matt wilkie
您可以使用/b开关将其放在同一个窗口中。 - Romário
当然!我不知道为什么我没想到结合/I/B选项。但你可能也想要/WAIT选项,否则共享控制台会变得非常混乱。 - dbenham
start /i cmd仍然保留父cmd窗口的原始%path%,是吗?那不是问题所想要的。 - Pacerier
@Pacerier - PATH 变量与其他变量没有任何区别。新的 cmd 会话将忽略父会话的当前环境,而是使用在启动父会话时存在的值。我已经承认这不完全是 OP 所寻找的,因为父会话可能没有使用默认值启动。但这个答案是朝着正确方向迈出的一步,在许多情况下是足够的。 - dbenham

1
以下是我如何使它对我起作用的示例。
@ECHO OFF
TITLE Test
SET SELF=%~dp0%~n0%~x0

ECHO SetVar : %VAR1%
PAUSE
SETX VAR1 "Random Value"
WMIC PROCESS CALL CREATE "%SELF%"
EXIT 0

注意:%~dp0%~n0%~x0%~dpnx0相同,它们都等同于%~f0 - Stephan

-1

这是与 dbenham 或 matt wilkie 相同的解决方案。 - jeb
start/i by itself doesn't work isn't it? You need start/i %windir%/explorer "%windir%\system32\cmd.exe" - Pacerier

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