Board logo

标题: [其他] 将 setlocal 和 endlocal 之间的变量 传递到 endlocal 之后,变量的数量不定 [打印本页]

作者: newswan    时间: 2024-8-23 01:57     标题: 将 setlocal 和 endlocal 之间的变量 传递到 endlocal 之后,变量的数量不定

将 setlocal 和 endlocal 之间的变量 传递到 endlocal 之后
如果,变量的名字确定,比较好办。
但是如果变量的数量不定,变量名不定,不通过临时文件,还有什么办法?
谢谢
作者: Five66    时间: 2024-8-23 02:55

这样子么,不过得先弄成统一前缀格式的变量名,
可能不支持某些特殊字符,还有当前代码页不支持的字符会变成问号

for /f "tokens=1* delims==" %%a in ('set 变量名') do (
if "!aaa!" == "%aaa%" endlocal
set "%%a=%%b"
)
作者: qixiaobin0715    时间: 2024-8-23 08:54

回复 1# newswan
我想在endlocal前,使用call命令跳转到变量延迟开关外面,暂不执行endlocal,使用完变量后再关闭变量延迟开关,应当可行。
作者: aloha20200628    时间: 2024-8-23 10:06


有关 !var! 续命给 %var% 是批处的一个经典话题,其中有一个 ‘坑’ 就是不能在复合语块(例如 for 或 if 括号内部)中完成,原因是cmd预处理要对复合语块中的所有 %var% 预赋值...

作者: qixiaobin0715    时间: 2024-8-23 10:27

本帖最后由 qixiaobin0715 于 2024-8-23 10:32 编辑

个人不喜欢call、goto这样的跳转命令,所以也不是太懂,大概就是这样子:
  1. @echo off
  2. for /l %%i in (1,1,3) do (
  3.     set str=%%i
  4.     setlocal enabledelayedexpansion
  5.     set str=0!str!
  6.     call :o
  7.     endlocal
  8. )
  9. echo,####%str%
  10. pause
  11. :o
  12. echo,%str%
复制代码

作者: 77七    时间: 2024-8-23 12:38

  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "useback delims=" %%a in ("%~f0") do (
  4. set /a _n+=1
  5. set _!_n!=%%a
  6. )
  7. for /f "delims=" %%a in ('set _') do (
  8. if not defined _end (
  9. endlocal
  10. set _end=1
  11. )
  12. set %%a
  13. )
  14. set _end=
  15. set _
  16. pause
复制代码

作者: aloha20200628    时间: 2024-8-23 14:03

本帖最后由 aloha20200628 于 2024-8-23 14:38 编辑


运行以下代码可见》4种实况说明 ‘延迟变量’ 开启前后的值变及其 ‘续命’ 的一个有效方法...
  1. @echo off &setlocal
  2. echo,第一轮
  3. set/a "v1=1,v2=2,v3=3"
  4. echo, 未开延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  5. setlocal enabledelayedexpansion
  6. set "v1=11" &set "v2=12" &set "v3=13"
  7. echo, 开启延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  8. endlocal
  9. echo, 关闭延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  10. echo,第二轮(续命之道)
  11. set/a "v1=1,v2=2,v3=3"
  12. echo, 未开延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  13. setlocal enabledelayedexpansion
  14. set "v1=21" &set "v2=22" &set "v3=23"
  15. echo, 开启延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  16. endlocal & set "v1=%v1%" &set "v2=%v2%" &set "v3=%v3%"
  17. echo, 关闭延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  18. echo,第三轮(复合语块-if)
  19. set/a "v1=1,v2=2,v3=3"
  20. echo, 未开延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  21. if 1 equ 1 (
  22. setlocal enabledelayedexpansion
  23. set "v1=31" &set "v2=32" &set "v3=33"
  24. echo, 开启延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  25. endlocal & set "v1=%v1%" &set "v2=%v2%" &set "v3=%v3%"
  26. )
  27. echo, 关闭延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  28. echo,第四轮(复合语块-for)
  29. set/a "v1=1,v2=2,v3=3"
  30. echo, 未开延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  31. for /l %%n in (1,1,1) do (
  32. setlocal enabledelayedexpansion
  33. set "v1=41" &set "v2=42" &set "v3=43"
  34. echo, 开启延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  35. endlocal & set "v1=%v1%" &set "v2=%v2%" &set "v3=%v3%"
  36. )
  37. echo, 关闭延迟变量》v1=%v1%,v2=%v2%,v3=%v3%
  38. endlocal&pause&exit/b
复制代码

作者: qixiaobin0715    时间: 2024-8-23 14:59

回复 7# aloha20200628
第2轮好牌!!!
前不久我也遇到过这种情况,都是使用临时文件中转的,好麻烦。这个有用!简洁!
作者: qixiaobin0715    时间: 2024-8-23 15:16

本帖最后由 qixiaobin0715 于 2024-8-23 15:46 编辑

要注意,5楼代码跳转后,执行标签后面的代码时,变量延迟开关并没有关闭,返回时才关闭,每个完整循环完成关闭一次,特殊情况时有用。
作者: 77七    时间: 2024-8-23 15:58

本帖最后由 77七 于 2024-8-23 16:00 编辑

成也预处理,败也预处理,老手在利用预处理;新手败给预处理
  1. setlocal
  2. set n=1
  3. endlocal & echo %n%
复制代码

  1. set n=1& echo %n%
复制代码



变量延迟扩展表示不服,爱用哪个就用哪个;关闭,只能用预处理

  1. set n=1& setlocal enabledelayedexpansion &echo %%n%%=[%n%](预处理=空值)  ^^!n^^!=[!n!](变量延迟扩展值)&endlocal & echo set "n=%n%"
复制代码

作者: idwma    时间: 2024-8-23 17:05

不能把endlocal这道箍摘掉吗
作者: buyiyang    时间: 2024-8-23 19:15

不临时存储上一个环境的变量的话,应该是没有办法了。
正如你所言,如果变量的名称确定比较好办,可以利用预处理针对性复制变量;但是如果变量的数量不定名称不定,意味着要完整复制变量环境,而这明显违背了使用 endlocal 的初心:
既要用 endlocal 结束本地化变量环境,然后又要该环境的所有变量,不就相当于没用 endlocal 吗?好奇你的应用场景是什么,如楼上所言何不摘掉 endlocal 呢?
作者: HOPE2021    时间: 2024-8-24 06:59

本帖最后由 HOPE2021 于 2024-8-24 07:25 编辑

回复 12# buyiyang
如果使用变量构造命令的话,应该可以,不过这对兼容性有很大压力(不同版本的 Windows 变量内容长度限制不同),所以不可能一次性复制大量变量,也难以复制特殊字符。

既要用 endlocal 结束本地化变量环境,然后又要该环境的所有变量,不就相当于没用 endlocal 吗?好奇你的应用场景是什么,如楼上所言何不摘掉 endlocal 呢?

其实不是,有些复杂运算可能需要启用延缓环境变量,但是程序的主体不需要,所以才有这种看似“奇怪”的需求。

以下命令在 Windows 2000(版本 5.00.2195)和 Windows 11(Microsoft Windows [版本 10.0.22631.4037])测试通过,简单地实现了这种需求,仅作抛砖引玉。
  1. @echo off
  2. SetLocal EnableDelayedExpansion
  3. set /a var1=1,var2=2,var3=3
  4. echo.已启用延缓环境变量
  5. echo.var1 = !var1!, var2 = !var2!, var3 = !var3!
  6. %================================================================%
  7. %= 设定要复制的变量名列表 =%
  8. set "__varlist=var1 var2 var3"
  9. set "__command="
  10. for %%_ in (!__varlist!) do (
  11.     set "__command=!__command! "%%_=%%%%_%%""
  12. )
  13. for /f "delims=" %%_ in ('echo.!__command!') do (
  14.     echo.{%%_}
  15.     endlocal
  16.     for %%` in (%%_) do (
  17.         set %%`
  18.     )
  19. )
  20. %================================================================%
  21. set __varlist
  22. set __command
  23. echo.已禁用延缓环境变量
  24. echo.var1 = %var1%, var2 = %var2%, var3 = %var3%
  25. %= 下面一行没有任何意义,只是想说明已禁用延缓环境变量 =%
  26. echo.var1 = !var1!, var2 = !var2!, var3 = !var3!
  27. pause
复制代码

作者: buyiyang    时间: 2024-8-24 08:58

回复 13# HOPE2021

我以为楼主只用SetLocal,用SetLocal EnableDelayedExpansion的话因为处理特殊字符!和^麻烦确实有时候需要关闭,要是批处理能使用变量延迟又不变量局域化就好了。
如果要复制的变量名称和数量不确定的话,应该需要像2楼那样用set列出所有变量列表。
作者: newswan    时间: 2024-8-25 03:47

回复 6# 77七

这个思路用过。在 setlocal 的时候
  1. set _inlocal_=True
复制代码

作者: newswan    时间: 2024-8-25 04:03

如果比较复杂的,setlocal 隔离还是必要的,虽然隔离要麻烦些。
我现在遇到的一个情况是,以前有一个脚本,单独使用的,现在要在另一个脚本里调用,两个脚本里有变量冲突。
认为,与其改变量名,不如 setlocal ,仅仅返回需要的变量,把输出的变量名,前面加一个前缀,_a_var1
用一个通用的办法比较好些。
作者: newswan    时间: 2024-8-25 04:05

本帖最后由 newswan 于 2024-8-25 04:10 编辑

回复 13# HOPE2021

变量名 变量值 不够多的时候,可以,一般的脚本不会有那么多输出。
可以用把参数的方式返回。调用的地方获取整个字符串后,再进行处理。
作者: newswan    时间: 2024-8-25 04:13

回复 3# qixiaobin0715

代码位置是在 endlocal 之外,但是逻辑上还是在 endlocal 运行之前
作者: newswan    时间: 2024-8-25 04:39

本帖最后由 newswan 于 2024-8-26 09:55 编辑

最后选择这种方式
a.bat
  1. @echo off
  2. setlocal
  3. set _var1_=111
  4. call b.bat
  5. echo :1
  6. set _
  7. for /f "usebackq tokens=1,* delims==" %%a in (`set __`) do (
  8. set _b%%a=%%b
  9. set %%a=
  10. )
  11. echo :2
  12. set _
  13. endlocal
  14. pause
  15. exit/b
复制代码
b.bat
  1. @echo off
  2. setlocal enableDelayedExpansion
  3. set __var1__=value1
  4. set __var2__=value2
  5. set __var3__=value3
  6. for /f "delims=" %%a in ('set __') do (
  7. if defined _inLocal_ (endlocal)
  8. set %%a
  9. )
  10. exit/b
复制代码
个人习惯,变量用 "_" 开头,要输出的变量,用 "__" 开头
作者: aloha20200628    时间: 2024-8-25 10:49

本帖最后由 aloha20200628 于 2024-8-25 10:56 编辑

回复 1# newswan

主批通过 call 调用子批,其实是共享主批的变量环境,除非子批用 setlocal [...] 独建自己的本地变量环境,但子批最后将本地变量 ‘续命’ 给主批,还是要避免与主批的同类同名变量的覆盖问题,因此,多个子批被主批通过 call 调用的一个简捷而稳妥之策,是由主批给每个子批分配共享变量名区段(子批独建的本地变量除外)用前缀或后缀或流水号标识等方法,不必再绕经每个子批的 ‘续命’
弯道,此法不仅是针对批处,其他脚本依然... 仅供参考





欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2