[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[文本处理] 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熊” 都是正常的字符串呀。为什么会这样,真让人感到困惑。

求高人讲解一下。非常感谢!

回复 1# mokson


变量延迟扩展共有两步:

  • setlocal enabledelayedexpansion
  • !T!

你少了一步
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set "T=abc" & echo !T!
  4. pause
复制代码
注,以上代码只是为了演示语法,正经的代码应该这样写:
  1. @echo off
  2. set "T=abc"
  3. echo %T%
  4. pause
复制代码
测试代码之前请做好备份

TOP

本帖最后由 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对计算表达式的处理机制有点另类,只能从实测结果来逐点推断其内部逻辑了...

TOP

本帖最后由 77七 于 2024-6-29 16:17 编辑

2熊和熊2 分别作为变量,是一个整体,应该不会解释为 数字2和变量熊,并拼接为一个整体,无需用引号包裹。只是2熊这个变量有点特殊,它以数字开头,与批处理中的自有变量 %2存在冲突,它的使用范围有限,如果冒然省略了变量的包裹符号,报错进制错误,被认为是其它进制数字了。

比如 无论是否定义了变量 2熊,set v=%2熊%,批处理在预处理时,会优先扩展 %2 然后剩下“熊”和“%”,如果%2为空,v的值为 熊,如果非要使用 2熊 这个变量,如set 2熊=7,只能在开启变量延迟扩展的情况下使用!!包裹变量使用。
  1. @echo off
  2. set 熊=3
  3. set 2熊=7
  4. set 熊3=19
  5. setlocal enabledelayedexpansion
  6. echo [%2熊%] and [!2熊!]
  7. setlocal
  8. set /a V=2熊*1
  9. echo !V!
  10. endlocal
  11. echo ------
  12. setlocal
  13. set /a V=%2熊%*3
  14. echo !V!
  15. endlocal
  16. echo ------
  17. setlocal
  18. set /a V=!2熊!*2
  19. echo !V!
  20. endlocal
  21. pause
复制代码

第二个例子中 如果%2 为空,%*也为空,则执行的是 set /a v=熊3,结果为变量熊3的值,不过在这种错误的情况下,如果用引号包裹变量,set /a V="%2熊%"*3,隔断了 %与*,结果又不相同。
bat小白,请多指教!谢谢!

TOP

本帖最后由 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,变量引用符与数字相连则用相互链接结果参与运算

以上验证的一个简单结论》最好不用数字开头命名的变量参与运算表达式,非要用则须用变量引用符%或!包裹而且其值非空

TOP

回复 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"转化为长整型,将指针移到产生错误的"熊"处并继续解析,因为数字后紧接着的不是分隔符或操作符,会提示无效数字错误。

TOP

回复 6# buyiyang

随手在回复栏中写成居然慢待了set/a...
有兴趣者可用此题玩一把‘数字游戏’,但观者还是要看一个简明结论...

TOP

有个规则 ,变量名 不能以数字开头

TOP

set /a 2熊
2熊以数字2开头,"2熊"被认为是一个数值;因为"熊"不是一个有效数字,所以报错;

set /a 熊2
熊2以非数字开头,"熊2"被认为是一个变量;因为"熊2"这个变量未赋值(空值),被认为是0

TOP

不知这样的结果如何解释:
  1. @echo off
  2. set n=2a
  3. set /a n=n
  4. echo,%n%
  5. pause
复制代码

TOP


验证一下10楼的示例,歪打正着,看出了另一道捷径》
   set a=123.456
   set/a a=a 》返回值=123
   可用此法一步删除小数部分
   set "a=123中文xyz"
   set/a a=a 》返回值=123
   可用此法一步删除纯数字之后的字符串

TOP

TOP

本帖最后由 aloha20200628 于 2024-7-2 12:27 编辑

回复 12# qixiaobin0715

有看‘掐头’,有看‘去尾’,一鱼两吃,各得其趣...

TOP

回复 10# qixiaobin0715

set n=2a 是直接字符串形式存储的,set /a n=n 是先解析再变量名取值最后计算的,
set /a n=2a 在解析阶段就不通过,而set /a n=n 在取值阶段没有合法性的判断。
参考取值阶段的部分源码:
  1. LONG
  2. PopOperand( VOID )
  3. {
  4.     TCHAR *wptr;
  5.     LONG result;
  6.     if (iOperand == 0)
  7.         return 0;
  8.     result = lOperands[ --iOperand ].Value;
  9.     if (wptr = MyGetEnvVarPtr(lOperands[ iOperand ].Name)) {
  10.         while (*wptr)
  11.             if (*wptr <= SPACE || *wptr == QUOTE)
  12.                 wptr += 1;
  13.             else
  14.                 break;
  15.         result = _tcstol(wptr, &wptr, 0);
  16.     }
  17.     return result;
  18. }
复制代码
result = _tcstol(wptr, &wptr, 0);wptr是字符串"2a",通过_tcstol将字符串"2a"转化为长整型数字2作为返回,然后wptr是剩余的"a"。
它会进行最大限度的匹配和转化,比如"086"判断为八进制转化为0,剩余86。
set /a n=086 则会提示无效数字错误是因为解析阶段还有一个有效性的判断:
  1. if (_istdigit(*tas) || _istalpha(*tas)) {
  2.                 rc = MSG_SET_A_INVALID_NUMBER;
  3.                 break;
  4.             }
复制代码
"086"判断为八进制转化为0,剩余86,下一位字符是数字则提示无效数字;"2a"判断为十进制转化为2,剩余a,下一位字符是字母也提示无效数字。

TOP

连在一起不行就分开来
set /a是数学的运算 ,不要有中文
不懂的就不要用

TOP

返回列表