标题: [系统相关] Setlocal enabledelayedexpansion放在哪个位置最好? [打印本页]
作者: 踏沙行 时间: 2018-12-5 09:05 标题: Setlocal enabledelayedexpansion放在哪个位置最好?
记得之前在论坛中看过一篇文章,说把Setlocal enabledelayedexpansion和 endlocal放在复合语句内部最正确,但是按这种方式操作,却接连出现问题。十分迷茫。
现在主要想弄清楚两个问题:
Setlocal enabledelayedexpansion和 endlocal,放在哪个位置得到的结果,“准确”且“效率高”?
(1)复合语句括号之外
(2)复合语句括号之内
-------------------------------
已知文件%tmp1%内容如下:- HKEY_CURRENT_USER\Temp Num\Temp1
- String 1
- String 2
- String 3
- String 4
- Stringabcdefghigklmnopqrstuvwsyz 1
- Stringabcdefghigklmnopqrstuvwsyz 2
- Stringabcdefghigklmnopqrstuvwsyz 3
- Stringabcdefghigklmnopqrstuvwsyz 4
- String abcdefghigklmnopqrstuvwsyz str1
- String abcdefghigklmnopqrstuvwsyz str2
- String abcdefghigklmnopqrstuvwsyz str3
- String abcdefghigklmnopqrstuvwsyz str4
- HKEY_CURRENT_USER\Temp Num\Temp2
- String 1
- String 2
- String 3
- String 4
- Stringabcdefghigklmnopqrstuvwsyz 1
- Stringabcdefghigklmnopqrstuvwsyz 2
- Stringabcdefghigklmnopqrstuvwsyz 3
- Stringabcdefghigklmnopqrstuvwsyz 4
- ……(后面类似,省略不写)……
复制代码
1、把Setlocal enabledelayedexpansion和 endlocal放在复合语句内部,代码和结果如下:- ===========代码:========
- set "tmp1=%temp%\t1.txt"
- for /f "delims=" %%a in ('type %tmp1%') do (
- Setlocal enabledelayedexpansion
- set "str=%%a"
- if /i "!str:~,5!"=="HKEY_" (set "sKey=%%a") else (set "sval=%%a")
- if defined sval (echo,!sKey!---!sval! &set "sval=")
- endlocal
- )
- pause&exit
-
- ======结果==========
- ---String 1
- ---String 2
- ---String 3
- ---String 4
- ---Stringabcdefghigklmnopqrstuvwsyz 1
- ---Stringabcdefghigklmnopqrstuvwsyz 2
- ---Stringabcdefghigklmnopqrstuvwsyz 3
- ---Stringabcdefghigklmnopqrstuvwsyz 4
- ---String abcdefghigklmnopqrstuvwsyz str1
- ---String abcdefghigklmnopqrstuvwsyz str2
- ---String abcdefghigklmnopqrstuvwsyz str3
- ---String abcdefghigklmnopqrstuvwsyz str4
- ---String 1
- ---String 2
-
- ====结论=====
- 部分变量并未得到延迟,结果为空
复制代码
2、把Setlocal enabledelayedexpansion和 endlocal放在复合语句外部,代码和结果如下:- ===========代码:========
- set "tmp1=%temp%\t1.txt"
- Setlocal enabledelayedexpansion
- for /f "delims=" %%a in ('type %tmp1%') do (
- set "str=%%a"
- if /i "!str:~,5!"=="HKEY_" (set "sKey=%%a") else (set "sval=%%a")
- if defined sval (echo,!sKey!---!sval! &set "sval=")
- )
- endlocal
- pause&exit
-
- ======结果==========
- HKEY_CURRENT_USER\Temp Num\Temp1---String 1
- HKEY_CURRENT_USER\Temp Num\Temp1---String 2
- HKEY_CURRENT_USER\Temp Num\Temp1---String 3
- HKEY_CURRENT_USER\Temp Num\Temp1---String 4
- HKEY_CURRENT_USER\Temp Num\Temp1---Stringabcdefghigklmnopqrstuvwsyz 1
- HKEY_CURRENT_USER\Temp Num\Temp1---Stringabcdefghigklmnopqrstuvwsyz 2
- HKEY_CURRENT_USER\Temp Num\Temp1---Stringabcdefghigklmnopqrstuvwsyz 3
- HKEY_CURRENT_USER\Temp Num\Temp1---Stringabcdefghigklmnopqrstuvwsyz 4
- HKEY_CURRENT_USER\Temp Num\Temp1---String abcdefghigklmnopqrstuvwsyz str1
- HKEY_CURRENT_USER\Temp Num\Temp1---String abcdefghigklmnopqrstuvwsyz str2
- HKEY_CURRENT_USER\Temp Num\Temp1---String abcdefghigklmnopqrstuvwsyz str3
- HKEY_CURRENT_USER\Temp Num\Temp1---String abcdefghigklmnopqrstuvwsyz str4
- HKEY_CURRENT_USER\Temp Num\Temp2---String 1
- HKEY_CURRENT_USER\Temp Num\Temp2---String 2
- HKEY_CURRENT_USER\Temp Num\Temp2---String 3
- HKEY_CURRENT_USER\Temp Num\Temp2---String 4
-
- ====结论=====
- 变量得到延迟,结果正确
复制代码
补充疑问:
如果把在复合语句内部再开启变量延迟开关是正确的,那么在for循环中,每执行一句就要开关一次,岂不是更消耗资源吗?
作者: Batcher 时间: 2018-12-5 09:18
那么在for循环中,每执行一句就要开关一次,岂不是更消耗资源吗?
你循环执行1万次,看看到底慢多少?
作者: 踏沙行 时间: 2018-12-5 09:22
回复 2# Batcher
关键是放在内部无法得到执行结果啊
作者: Batcher 时间: 2018-12-5 09:59
回复 3# 踏沙行
一个连基本功能都无法实现的代码,何谈好或不好呢。
作者: 踏沙行 时间: 2018-12-5 10:16
回复 4# Batcher
我是怀疑,我把变量延迟开关放在内部的写法,是不是哪里出错了?
也许,放在内部是最好的,但我没有写对。
另外,有时候放在内部结果正确,有时候结果出错。
不知道是哪里的问题?
作者: flashercs 时间: 2018-12-5 15:10
论坛写的不一定都是正确的,所谓 "尽信书不如无书";实践是检验真理的唯一标准.
做实验最好把@echo on 打开,看看cmd的指令到底是执行的啥玩意!!!
每个for语句其实是一行指令,别看分行写,其实是一个指令;在执行该指令前,若没有开启变量延迟,那么cmd会对指令中的每个变量进行预赋值;若开启了变量延迟,cmd不会对变量进行预赋值;
你的第一个例子setlocal放在for内部的时候;在每个for指令前,未开启变量延迟,故变量if /i "!str:~,5!"=="HKEY_"一直是false,所以set skey=%%a就不会执行的.!skey!一直是空值.
第二个setlocal放到for外面,由于开启了变量延迟,在每次for指令执行前,str不会被预赋值,故能得到正确结果.
作者: flashercs 时间: 2018-12-5 15:29
本帖最后由 flashercs 于 2018-12-5 15:46 编辑
cmd踩的坑多了去了.就像以前某论坛写cmd里stdout 的字符都是Unicode编码,一直信以为真,后来踩了坑才知道是错误的.
type和more都可以识别ansi和Unicode-BOM编码的文本,但type输出到console的文本编码与源文件一致,而more输出到控制台的编码是ANSI的,它是转码的;
findstr是按ANSI编码搜索字符串的,忽视文本编码,而find却可以正确识别编码.
dir 对于office文档格式的文件搜索是不准确的,比如dir *.doc ,搜索结果会包括.doc,.docx,.doct,.docm...等任何扩展名以.doc开头的文件.
管道|,在不指定Unicode输出时,| 前面的指令会转码成ANSI,这就会导致一些Unicode文件名的识别失败,虽然几率很小.比如dir /a /b /s *.txt|cscript -nologo a.js;让a.js来处理stdin 文本,但是若文件名含有无法转换成ANSI的Unicode字符就会变成?,比如文件名 "1👀💋🎶🚗0.txt",就会变成了"1????????0.txt",这显然是找不到该文件的.
这都是坑.不踩过能知道?
作者: 踏沙行 时间: 2018-12-5 18:00
回复 7# flashercs
谢谢!
楼上对bat了解的太深刻了,能知道各种坑,佩服佩服。
作者: tigerpower 时间: 2018-12-5 18:54
回复 5# 踏沙行
从setlocal到endlocal之间会创建一个新的局部环境,在这个局部环境中设置的变量在遇到endlocal时会被销毁。
当后面的循环需要用到前面循环设置的变量时,setlocal要放在循环之外。
当每一次循环都不希望受到上一次循环设置变量值的影响时,setlocal要放在循环之内。
你每次循环都要用到前面循环时设置的sKey和sval,所以setlocal要放在循环之外。
如果放在循环之内,在遇到endlocal时str, sKey和sval就都被销毁了。
作者: 踏沙行 时间: 2018-12-6 14:45
回复 9# tigerpower
谢谢~
欢迎光临 批处理之家 (http://www.bathome.net/) |
Powered by Discuz! 7.2 |