返回列表 发帖

[系统相关] 【已解决】不及时关闭延迟变量可能会导致 cd /d 操作失效


   近期回帖中遭遇一则怪相,在win8.1,win10系统可复现,用以下5段示例代码说明,唯独其中第5段代码中最后的 cd /d "c:\temp\" 失效,即脚本退出后当前目录仍是 "c:\temp\xxx"
   以下测试代码存为 test.bat 可在命令行直接用 test.bat n 来测试各段代码运行之后当前目录的结果,其命令行参数 n=1-5

@echo off &if "%~1"=="" (goto :5) else goto :%~1
:1
setlocal enabledelayedexpansion
cd "c:\temp\xxx"
echo,!cd!
cd "c:\temp\"
echo,改变当前目录有效》!cd!
endlocal
exit/b
:2
setlocal enabledelayedexpansion
cd "c:\temp\xxx"
echo,!cd!
cd "c:\temp\"
echo,改变当前目录有效》!cd!
endlocal
exit/b
:3
setlocal enabledelayedexpansion
cd "c:\temp\xxx"
echo,!cd!
endlocal
cd "c:\temp\"
echo,改变当前目录有效》%cd%
exit/b
:4
cd "c:\temp\xxx"
setlocal enabledelayedexpansion
echo,!cd!
endlocal
cd "c:\temp\"
echo,改变当前目录有效》%cd%
exit/b
:5
cd /d "c:\temp\xxx"
setlocal enabledelayedexpansion
echo,!cd!
cd /d "c:\temp\"
echo,!cd!》当前显示有效》脚本退出后无效
endlocal
exit/bCOPY
1

评分人数

    • Batcher: 感谢给帖子标题标注[已解决]字样PB + 2

看不懂 ,看起来像cd跟cd /d的区别 , cd不受endlocal影响 而 cd /d受endlocal影响???
反正不知道endlocal对内建变量的处理方式 , 基本上不会分开来用或者依赖endlocal之后的值 , 随便了

TOP

本帖最后由 aloha20200628 于 2025-1-9 15:50 编辑

回复 2# Five66

以前很多有关 cd 或 chdir 操作失效的原因概因未用 cd /d 参数,但本帖所谓的 cd /d 失效是与 setlocal enabeldelayedexpansion ... endlocal 有关,即一楼第五段代码运行后所示的 ‘怪状’ 》代码退出后当前目录并未被刚才调用过的 cd /d ... 操作有效改变...

TOP

SETLOCAL /?
开始批处理文件中环境改动的本地化操作。在执行 SETLOCAL 之后
所做的环境改动只限于批处理文件。要还原原先的设置,必须执
ENDLOCAL。达到批处理文件结尾时,对于该批处理文件的每个
尚未执行的 SETLOCAL 命令,都会有一个隐含的 ENDLOCAL 被执行。COPY

区域环境
bat小白,请多指教!谢谢!

TOP

回复 4# 77七

开关延迟变量的进退或生死变化应该是针对 !var! 变量吧,cd /d "c:\temp" 可是一个采用字面量(既非!var!亦非%var%)重置当前目录的实实在在的操作啊 ...

TOP

回复 5# aloha20200628


   setlocal enabledelayedexpansion 给它起个全名 区域环境和延迟变量扩展,貌似我们平时都只是 简单的说 延迟变量扩展,只强调了它的一个作用。
bat小白,请多指教!谢谢!

TOP

本帖最后由 flashercs 于 2025-1-9 19:51 编辑
:1
echo beforelocal cd=%cd%
setlocal enabledelayedexpansion
cd "c:\asp\abc"
echo,setlocal cd=!cd!
cd "c:\asp\"
echo,改变当前目录有效》!cd!
endlocal
echo,endlocal cd=%cd%
exit /b
:5
cd /d "c:\asp\abc"
echo beforelocal cd=%cd%
setlocal enabledelayedexpansion
echo,setlocal cd=!cd!
cd /d "c:\asp\"
echo,setlocal cd=!cd!》当前显示有效》脚本退出后无效
endlocal
echo,endlocal cd=%cd%
exit /bCOPY
endlocal cd始终等于beforelocal cd,没问题.
另一个脚本调用这个cdtest.bat
@echo off
cmd /c cdtest.bat 5
echo,cmd /c cd=%cd%
@REM call cdtest.bat 5
@REM echo,call cd=%cd%
pauseCOPY
cmd /c 与 call 脚本是有区别的.cmd /c 创建新的cmd进程,call 不会创建新cmd进程
微信:flashercs
QQ:49908356

TOP

本帖最后由 aloha20200628 于 2025-1-9 21:20 编辑


谢谢诸位跟帖讨论 回到问题的焦点,还是用以下代码说明吧...
@echo off
cd /d "c:\temp\xxx"
setlocal enabledelayedexpansion
echo,!cd!
cd /d "c:\temp\"
   echo,!cd!》改变当前目录有效
::以下两式会如实 ‘列表然后删除 "c:\temp\*.txt" 文件’
   dir /b/a-d *.txt
   del /q *.txt
endlocal
   :: 以下的操作不能想当然地认为当前目录还是 "c:\temp"
   echo,%cd%》自动复原为开启延迟变量前的当前目录
exit/bCOPY
可见》开启延迟变量后,无论 cd /d ... 或 dir /b/a-d ... 或 del ... 等操作结果都是如期而至,但关闭延迟变量后,当前目录会自动复原为开启延迟变量前的位置(但被删除的文件却是真的一去不复返了),有点出乎所料吧(先知先觉者除外 )以此为鉴,如从第10行之后续写代码时须谨记 ‘当前目录已被自动复原到开启延迟变量前的位置’...

TOP

可从源代码中窥见,本地化时当前目录、环境变量、命令扩展状态都会保存到当前批处理的数据结构体中,结束本地化还原。
// setlocal保存环境(省略简化、注释)
struct envdata *CopyEnv() ;
struct batsaveddata *p;
p->dircpy = mkstr(mystrlen(CurDrvDir)*sizeof(TCHAR)+sizeof(TCHAR)) ;
mystrcpy(p->dircpy, CurDrvDir) ;  // 保存当前目录
p->envcpy = CopyEnv() ;  // 保存当前环境变量
p->fEnableExtensions = fEnableExtensions;  // 保存当前命令扩展的启用状态
CurBat->saveddata[CurBat->numsavedenv] = p;  // 保存为当前索引批处理数据结构
CurBat->numsavedenv += 1;  // 索引递增
// endlocal恢复环境(省略简化、注释)
bdat->numsavedenv -= 1;  // 索引递减
p = bdat->saveddata[bdat->numsavedenv];
if (p) {
    // 如果当前盘与保存的盘符不同,则更改磁盘
    if (CurDrvDir[0] != (TCHAR)(c = _totupper(*p->dircpy)))
        ChangeDrive(c - (TCHAR) 0x40) ;
    ChangeDir(p->dircpy) ;  // 恢复保存的目录
    ResetEnv(p->envcpy) ;  // 恢复保存的环境变量
    fEnableExtensions = p->fEnableExtensions;  // 恢复保存的命令扩展状态
    bdat->saveddata[bdat->numsavedenv] = NULL ;
} ;COPY
1

评分人数

TOP

本帖最后由 aloha20200628 于 2025-1-9 23:05 编辑

回复 9# buyiyang


TOP

返回列表