【问题标题】:SETLOCAL ENABLEDELAYEDEXPANSION causes CD and PUSHD to not persistSETLOCAL ENABLEDELAYEDEXPANSION 导致 CD 和 PUSHD 不持久
【发布时间】:2014-12-02 11:29:38
【问题描述】:

我正在尝试在批处理脚本中同时使用 setlocal enabledelayedexpansioncd,这似乎不会将更改保留回 shell。

我需要setlocal enabledelayedexpansion 的原因是我需要在脚本运行时动态扩展脚本中的变量。

考虑下面的示例批处理文件:

a.bat
================================
setlocal enabledelayedexpansion
cd ..

上面的批处理文件没有像预期的那样迁移到上一个目录!

检查this

【问题讨论】:

  • 是什么让你说它不起作用?你能描述一下你的实际问题吗?
  • 参考帖子标题中提到的 PUSHD,我同意 CD 不会持续存在,但 pushd 会持续存在,不是吗?刚刚测试过;批处理完成后,我确实从命令行正确地弹出了。 (甚至是一对 POPD,每个都有效)

标签: batch-file windows-scripting


【解决方案1】:

问题在于setlocal 会导致任何当前目录更改都位于批处理文件的本地。

setlocal /?:

开始在批处理文件中本地化环境更改。 环境 发出 SETLOCAL 后所做的更改是批处理文件的本地内容。 必须发出 ENDLOCAL 才能恢复以前的设置。什么时候结束 达到一个批处理脚本,一个隐含的 ENDLOCAL 被执行任何 该批处理脚本发出的未完成的 SETLOCAL 命令。

当前目录包含在“环境更改”中。

试试这个,注意它在批处理中回显C:\ for %CD%,但是当批处理退出时,当前目录仍然重置。

[11:42:00.17] C:\temp
> cat test.bat
@echo off
setlocal enabledelayedexpansion
cd ..
echo %CD%
[11:42:19.38] C:\temp
> test.bat
C:\

[11:42:23.00] C:\temp
>

【讨论】:

  • 谢谢,我更明白了!那么,当我需要一个 setlocal 以便动态扩展变量时,有什么方法可以让 cd 在外部保持更改?
  • 您可以尝试显式调用endlocal,然后然后更改目录-或在调用setlocal之前更改目录。
  • @Blorgbeard:你的解释是错误的。当前目录包含在环境变量中,但它是一个动态变量,由 cmd.exe 以与 %DATE%、%TIME%、%RANDOM 相同的方式维护% 和别的;要确认这一点,只需输入:set。当前目录的这种行为是一种非常特殊的行为,据我所知,它是未记录的。见:this post
  • @Aacini 我并不是说当前目录实际上是一个环境变量,只是它是引用文档中所谓的“环境更改”的一部分。
  • @Blorgbeard:“环境”是存储环境变量的地方;关于这一点有很多信息。 SETLOCAL 描述是指环境变量的变化,当然除非你知道一个站点表明环境可能存储变量之外的其他数据,这是什么数据;例如,当前目录。
【解决方案2】:

这是它有效的证明 - 在根目录上方的任何文件夹中启动批处理文件。

@echo off
setlocal enabledelayedexpansion
echo "%cd%"
cd ..
echo "%cd%"
pause

【讨论】:

    【解决方案3】:

    Blorgbeard 解释了为什么在 SETLOCAL 之后发行的 CD 在 ENDLOCAL 之后不存在(可能是显式的或隐式的)。

    这是一个有趣的解决方法。 PUSHD 堆栈独立于由 SETLOCAL/ENDLOCAL 保存/恢复的环境。因此,以下简单序列将保留目录更改:

    @echo off
    setlocal
    cd somePath
    pushd .
    endlocal
    popd
    

    如果somePath 不变,则不是很有用 - 您可以在 ENDLOCAL 之后轻松发行 CD。但如果somePath 是动态的,它会非常有用。

    【讨论】:

    • 非常好,我们可以用它在 ENDLOCAL 中传递一个字符串吗?
    • @mosh - 当然,但前提是字符串是用户有权访问的有效规范文件夹路径。这对我来说似乎不是很有用。我想您可以在“标准”位置创建一个与您的字符串值相同的临时文件夹。然后,您可以提取您的字符串值并删除该文件夹。但这对于可以使用的字符有很大的限制。
    • @mosh - 但从支持返回多个字符串的角度来看,这是一个有趣的概念 - 每个返回值一个 PUSHD/POPD 对。
    • @mosh - 或者多个字符串可以作为具有单个 PUSHD/POPD 的分层文件夹链返回。
    • 请注意,如果希望其他驱动器在离开 setlocal 时也保留其当前目录,则需要对在 setlocal 期间修改当前目录的每个驱动器执行此解决方法。也许很明显,pushd 只会推动“。”这是执行时当前驱动器上的当前目录 - 忽略其他驱动器的目录,如果不注意,它们将恢复。
    【解决方案4】:

    跨 endlocal 将字符串保存在注册表中。在win7 cmd上测试(skip=2,可能因reg.exe版本不同而不同)

    :: What: stash string in registry from cmd line, across endlocal barrier
    :: PS. I added %RANDOM% to Windows-NT and Win95 command.com and still using it.
    @echo off
    
    set key="HKCU\Volatile Environment"
    set keyname=stash%RANDOM%
    set keytype=REG_SZ
    
    setlocal ENABLEDELAYEDEXPANSION
    
    ::== Save string
    set data_stash=Hello-%DATE%-%TIME%
    echo Saving=%data_stash% in key=%keyname%
    reg add %key% /v %keyname% /t %keytype% /d "%data_stash%" /f
    
    endlocal
    
    ::== Read string
    echo reg query %key% /v %keyname%
    for /f "tokens=2* skip=2" %%a in ('reg query %key% /v %keyname%') do (
      set data_unstashed=%%b
    )
    echo Read data_unstashed=%data_unstashed% 
    
    ::== Delete stash
    reg delete %key%  /v %keyname% /f  
    

    编辑斯蒂芬 保存endlocal以外的变量的另一种方法:

    @echo off
    set var1=hello
    set var2=world
    setlocal 
    echo previous: %var1%, %var2%
    set var1=HELLO
    set var2=WORLD
    echo inside:   %var1%, %var2%
    endlocal & set "var1=%var1%" & set "var2=%var2%" 
    echo after:    %var1%, %var2%
    

    诀窍在于,由于解析,变量被扩展之前 endlocal 有效,但setted 之后
    (很抱歉劫持了您的答案,但这也太大了,无法发表评论)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-20
      • 2012-03-14
      • 1970-01-01
      相关资源
      最近更新 更多