标题: [文本处理] Bat之困惑二三事,求解! [打印本页]
作者: mokson 时间: 2024-6-29 08:12 标题: Bat之困惑二三事,求解!
本帖最后由 mokson 于 2024-6-29 08:17 编辑
其一:set "T=abc" & echo %T%,不能得出结果,即使启用 Setlocal EnableDelayedExpansion 延迟扩展也无济于事,改&为&&也不行。要重复运行一次才有得出正常结果。
其二:set /a V="熊2"*1 运行结果是0没有问题的。但是,set /a V="2熊"*1 运行却报错。然而 “熊2” 和 “2熊” 都是正常的字符串呀。为什么会这样,真让人感到困惑。
求高人讲解一下。非常感谢!
作者: ShowCode 时间: 2024-6-29 10:46
回复 1# mokson
变量延迟扩展共有两步:
- setlocal enabledelayedexpansion
- !T!
你少了一步- @echo off
- setlocal enabledelayedexpansion
- set "T=abc" & echo !T!
- pause
复制代码
注,以上代码只是为了演示语法,正经的代码应该这样写:- @echo off
- set "T=abc"
- echo %T%
- pause
复制代码
作者: aloha20200628 时间: 2024-6-29 13:55
本帖最后由 aloha20200628 于 2024-7-1 09:23 编辑
回复 1# mokson
问题一。cmd对于复合语块(用括号包裹的for...或if...多行构成的完整语块)之外的代码是逐行解释执行;
未开启变量延迟时,每行代码先从左到右对其中每个%v%变量预先赋值,而且是 ‘取自处理本行之前’ 的%v%已有值或空值,因此,set "T=abc" & echo %T% 中的%T% 就会被预先赋予了空值(若此前未被赋值),但有例外,即在 set /a "x=1,y=x+1,z=y+1" 算式中,cmd会对其中的每个变量自动加持‘变量延迟’,并取消了其变量引用符%或!;
当开启变量延迟后,只有!v!变量才会有被延迟(或实时)赋值的效果,因此,set "T=abc" & echo !T! 会生效,而 set "T=abc" & echo %T% 会依然无效。
问题二。计算表达式中的非数值字符(包括中文字符)除各种运算语义符和变量引用符之外,皆被视为变量名,且可省略其变量引用符%或!;
但变量名若与数字直接相连则会被用零赋值(无论该变量是否已被赋值),前置相连被视为乘法,故 set /a x=熊2+1 结果x为1,后置相连 set /a x=2熊+1 则会被报错‘无效数字’;
但允许一个有值变量用字符串截取结果与数字后置相连再参与运算,如
set "熊=5"
set /a y=2%熊:~0,1%+1
结果y为26
总之,cmd对计算表达式的处理机制有点另类,只能从实测结果来逐点推断其内部逻辑了...
作者: 77七 时间: 2024-6-29 15:37
本帖最后由 77七 于 2024-6-29 16:17 编辑
2熊和熊2 分别作为变量,是一个整体,应该不会解释为 数字2和变量熊,并拼接为一个整体,无需用引号包裹。只是2熊这个变量有点特殊,它以数字开头,与批处理中的自有变量 %2存在冲突,它的使用范围有限,如果冒然省略了变量的包裹符号,报错进制错误,被认为是其它进制数字了。
比如 无论是否定义了变量 2熊,set v=%2熊%,批处理在预处理时,会优先扩展 %2 然后剩下“熊”和“%”,如果%2为空,v的值为 熊,如果非要使用 2熊 这个变量,如set 2熊=7,只能在开启变量延迟扩展的情况下使用!!包裹变量使用。
- @echo off
- set 熊=3
- set 2熊=7
- set 熊3=19
- setlocal enabledelayedexpansion
- echo [%2熊%] and [!2熊!]
-
- setlocal
- set /a V=2熊*1
- echo !V!
- endlocal
- echo ------
-
- setlocal
- set /a V=%2熊%*3
- echo !V!
- endlocal
- echo ------
-
- setlocal
- set /a V=!2熊!*2
- echo !V!
- endlocal
- pause
复制代码
第二个例子中 如果%2 为空,%*也为空,则执行的是 set /a v=熊3,结果为变量熊3的值,不过在这种错误的情况下,如果用引号包裹变量,set /a V="%2熊%"*3,隔断了 %与*,结果又不相同。
作者: aloha20200628 时间: 2024-6-30 12:06
本帖最后由 aloha20200628 于 2024-7-1 10:44 编辑
有关一楼问题二的进一步验证》
验证一。用 ‘非数字开头的变量名’ 参与运算
set "熊="
set/a x=熊+1 》x=1,与 set/a n+=1 算式中当n为空值时被自动赋零的结果一致,即把 ‘熊’ 视为一个被赋零的‘运算变量’
set "熊2="
set/a x=熊2+1 》x=1,与 set/a n+=1 算式中当n为空值时被自动赋零的结果一致,即把 ‘熊2’ 视为一个被赋零的‘运算变量’
set "熊=5"
set/a x=熊+1 》x=6,把 ‘熊’ 视为一个有值变量
set "熊2=5"
set/a x=熊2+1 》x=6,把 ‘熊2’ 视为一个有值变量
set/a x=2%熊2%+1 》x=26,变量引用符与数字相连则用相互链接结果参与运算
验证二。用 ‘数字开头的变量名’ 参与运算
set "2熊="
set/a x=2熊+1 》变量‘2熊’不被认为是有效的‘运算变量’,报错‘无效数字’
set/a x=2%2熊%+1 》有引用符的空值变量‘2熊’虽被认为是一个有效的运算变量(被自动赋零),但被报错‘零为除数’
set "2熊=5"
set/a x=2熊+1 》有值变量‘2熊’不被认为是有效的‘运算变量’,报错‘无效数字’
set/a x=%2熊%+1 》x=6,有引用符的有值变量‘2熊’被认为是一个有效的‘运算变量’
set/a x=2%2熊%+1 》x=26,变量引用符与数字相连则用相互链接结果参与运算
以上验证的一个简单结论》最好不用数字开头命名的变量参与运算表达式,非要用则须用变量引用符%或!包裹而且其值非空
作者: buyiyang 时间: 2024-6-30 16:33
回复 5# aloha20200628
后面的运算漏了/a开关,如4楼所言在脚本中%2会优先扩展,关于set /a如何解析表达式可以参考源码:http://www.bathome.net/redirect. ... 7438&pid=274374
实际是逐字符解析的,简单来说 set /a x=熊2+1 解析"熊2+1"时由于第一个字符既不是数字也不是操作符,会从熊始到分隔符或操作符止将熊2视作变量名。set /a x=2熊+1 解析"2熊+1"时由于第一个字符是数字,会将"2熊+1"转化为长整型,将指针移到产生错误的"熊"处并继续解析,因为数字后紧接着的不是分隔符或操作符,会提示无效数字错误。
作者: aloha20200628 时间: 2024-6-30 16:55
回复 6# buyiyang
随手在回复栏中写成居然慢待了set/a...
有兴趣者可用此题玩一把‘数字游戏’,但观者还是要看一个简明结论...
作者: newswan 时间: 2024-6-30 20:41
有个规则 ,变量名 不能以数字开头
作者: WHY 时间: 2024-7-1 23:58
set /a 2熊
2熊以数字2开头,"2熊"被认为是一个数值;因为"熊"不是一个有效数字,所以报错;
set /a 熊2
熊2以非数字开头,"熊2"被认为是一个变量;因为"熊2"这个变量未赋值(空值),被认为是0
作者: qixiaobin0715 时间: 2024-7-2 09:03
不知这样的结果如何解释:- @echo off
- set n=2a
- set /a n=n
- echo,%n%
- pause
复制代码
作者: aloha20200628 时间: 2024-7-2 11:55
验证一下10楼的示例,歪打正着,看出了另一道捷径》
set a=123.456
set/a a=a 》返回值=123
可用此法一步删除小数部分
set "a=123中文xyz"
set/a a=a 》返回值=123
可用此法一步删除纯数字之后的字符串
作者: qixiaobin0715 时间: 2024-7-2 12:10
回复 11# aloha20200628
http://bbs.bathome.net/thread-60321-1-1.html
作者: aloha20200628 时间: 2024-7-2 12:25
本帖最后由 aloha20200628 于 2024-7-2 12:27 编辑
回复 12# qixiaobin0715
有看‘掐头’,有看‘去尾’,一鱼两吃,各得其趣...
作者: buyiyang 时间: 2024-7-2 17:04
回复 10# qixiaobin0715
set n=2a 是直接字符串形式存储的,set /a n=n 是先解析再变量名取值最后计算的,
set /a n=2a 在解析阶段就不通过,而set /a n=n 在取值阶段没有合法性的判断。
参考取值阶段的部分源码:- LONG
- PopOperand( VOID )
- {
- TCHAR *wptr;
- LONG result;
-
- if (iOperand == 0)
- return 0;
-
- result = lOperands[ --iOperand ].Value;
- if (wptr = MyGetEnvVarPtr(lOperands[ iOperand ].Name)) {
-
- while (*wptr)
- if (*wptr <= SPACE || *wptr == QUOTE)
- wptr += 1;
- else
- break;
-
- result = _tcstol(wptr, &wptr, 0);
- }
-
- return result;
- }
复制代码
result = _tcstol(wptr, &wptr, 0);wptr是字符串"2a",通过_tcstol将字符串"2a"转化为长整型数字2作为返回,然后wptr是剩余的"a"。
它会进行最大限度的匹配和转化,比如"086"判断为八进制转化为0,剩余86。
set /a n=086 则会提示无效数字错误是因为解析阶段还有一个有效性的判断:- if (_istdigit(*tas) || _istalpha(*tas)) {
- rc = MSG_SET_A_INVALID_NUMBER;
- break;
- }
复制代码
"086"判断为八进制转化为0,剩余86,下一位字符是数字则提示无效数字;"2a"判断为十进制转化为2,剩余a,下一位字符是字母也提示无效数字。
作者: Five66 时间: 2024-7-2 23:49
连在一起不行就分开来
set /a是数学的运算 ,不要有中文
不懂的就不要用
作者: qixiaobin0715 时间: 2024-7-3 08:16
回复 14# buyiyang
欢迎光临 批处理之家 (http://www.bathome.net/) |
Powered by Discuz! 7.2 |