Board logo

标题: 【项目1】批处理函数库调用接口的约定 [打印本页]

作者: lllsoslll    时间: 2012-6-1 18:16     标题: 【项目1】批处理函数库调用接口的约定

本帖最后由 lllsoslll 于 2012-6-1 18:53 编辑

走自己的路, 让别人紧随其后。。。
如题,本贴主要讨论:
           子过程(函数)调用接口的多样化问题,
      目的:
           为接口约定一个标准, 方便使用者调用和编写者高效实现;
作者: lllsoslll    时间: 2012-6-1 18:39

接口约定,涉及到全局变量约定(对使用者不可见); 讨论的问题很多。。。
作者: applba    时间: 2012-6-1 21:13

只要注释写得好,问题不大……
作者: plp626    时间: 2012-11-11 00:25

仿照c语言结合批处理做个约定:

无论什么子过程必须有返回值,错误代码保存在全局变量bat_errno中

返回变量名为第一个实参名
作者: Demon    时间: 2012-11-11 01:35

仿照c语言结合批处理做个约定:

无论什么子过程必须有返回值,错误代码保存在全局变量bat_errno中

返 ...
plp626 发表于 2012-11-11 00:25


所有C标准库函数都会设置errno?
作者: plp626    时间: 2012-11-11 11:57

回复 5# Demon


我没这么说,
    你是不是理解错了?
作者: Demon    时间: 2012-11-11 13:22

回复  Demon


我没这么说,
    你是不是理解错了?
plp626 发表于 2012-11-11 11:57



    算我理解错了。
作者: qzwqzw    时间: 2012-11-12 16:55

本帖最后由 qzwqzw 于 2012-11-12 17:20 编辑

建议用固定的环境变量来做子函数返回值
比如bat_ret
或者使用子函数的标签名作为返回值变量名
如果“返回变量名为第一个实参名”
那么子函数在处理不定个数的参数数组时就会碰到麻烦
另外在标准化原有的函数时
如果实参已被挪作它用
处理起来也会碰到麻烦

另外需要考虑几个问题
1、子函数内的环境变量是否除了返回值变量全部setlocal
2、子函数内发生错误是否统一抛出处理?错误抛出后是在子程序中终止退出还是在主程序中?
3、子函数内是否需要一个指示程序自身路径的变量%0%,以解决用%0递归调用自身时的问题?
作者: CrLf    时间: 2012-11-12 18:40

本帖最后由 CrLf 于 2012-11-12 18:43 编辑

回复 8# qzwqzw


赞同“用固定的环境变量来做子函数返回值”,不太赞同“使用子函数的标签名作为返回值变量名”,cmd 中的变量读取效率如此蛋疼,还是能减少尽量少吧,而且应该使用在变量表中次序靠后的变量名,甚至用 %cmdcmdline% 都可以
-----------------------------------------------------------
如果“返回变量名为第一个实参名”
那么子函数在处理不定个数的参数数组时就会碰到麻烦

有点鱼与熊掌的味道,这看法我很赞同,但有时返回值不止一个就很头疼了
-----------------------------------------------------------
子函数内的环境变量是否除了返回值变量全部setlocal

窃以为这是在子函数需要产生临时变量时必须的,对全局变量的操作通过其他方法另行转存(除非变量中含换行符,否则应该能处理所有情况)
-----------------------------------------------------------
子函数内发生错误是否统一抛出处理?错误抛出后是在子程序中终止退出还是在主程序中?

另,建议返回值也以环境变量形式存在,即类似 bat_err 之类的变量名(最好还能短一点?比如 _err ?观察比较各种字符,似乎 _ 在表中名次比较靠后)
-----------------------------------------------------------
子函数内是否需要一个指示程序自身路径的变量%0%,以解决用%0递归调用自身时的问题

不赞同,首先以数字开头的变量名就是大忌,必须要用变量延迟才能避免错误解析,其次我实在想不到一个子函数在什么情况非得递归父脚本?
作者: plp626    时间: 2012-11-12 21:51

本帖最后由 plp626 于 2012-11-12 22:34 编辑

函数返回值
用固定的环境变量名来做子函数返回值变量名
优点是:  便于管理,思路清晰,可借鉴 C语言 函数栈帧思路,解决后续很多问题。。
缺点是:  讲这个固定变量名作为传递运算结果的“中介”,赋值多了一次,相对来说效率低了一点

第一个实参名作为返回变量名
优点是: 传递结果只进行一次赋值操作,一步到位,效率高
缺点是: 过于灵活,使得问题复杂化,比如,如果每次调用时第一个参数名给的变量名都不同,可能产生很多临时变量,后续问题一大堆,还没想清楚,待大家讨论


setlocal 的讨论
先看看大家对于返回多个结果这个问题的解决办法,再说。。

程序自身路径:
这是个棘手的问题,如果多个函数调用,就得用setlocal来解决, 所以还是看大家对setlocal的讨论
作者: qzwqzw    时间: 2012-11-13 09:51

本帖最后由 qzwqzw 于 2012-11-13 10:30 编辑

“使用子函数的标签名作为返回值变量名”所带来的变量遍历效率问题
与程序中实际调用的子函数的个数有线性关系
在该个数未达到一定的数量级时对效率的影响应该比较有限
当然如何应用标签名以及如何处理标签名中的:是个需要考虑的问题

“固定变量名作为传递运算结果的“中介”,赋值多了一次”
如果是海量级的递归调用确实会有比较显著的影响
此种情况下应该允许该函数使用参数作为函数返回值变量名
此种情况可以看作是函数使用了一个变量指针作为实参
不将它看作返回值变量
因此不会影响整体的函数调用接口的约定

“对于返回多个结果”
可以参考一般高级语言的几种处理方式
一是将所有结果以伪数组的方式存入单一环境变量
  1. setlocal
  2. set test1=value1
  3. set test2=value2
  4. set test3=value3
  5. endlocal & set ret=%test1% %test2% %test3%
  6. for %%e in (%ret%) do echo %%e
复制代码
二是将所有结果的指针存入单一环境变量
  1. setlocal
  2. set test1=value1
  3. set test2=value2
  4. set test3=value3
  5. endlocal & (
  6. set "ret=test1 test2 test3"
  7. set test1=%test1%
  8. set test2=%test2%
  9. set test3=%test3%
  10. )
  11. setlocal EnableDelayedExpansion
  12. for %%e in (%ret%) do echo %%e=!%%e!
复制代码
“指示程序自身路径的变量”
这个可以不用%0%
可以使用其它约定的名称
这种用法也是我发帖时临时构造的
没有考虑到解析错误的问题

“子函数在什么情况非得递归父脚本?”
这分为两个方面
1-%0是用来递归调用子函数自身而非父脚本
这在子函数内部用到shift处理参数数组时可能会用到
当然这种情况一般在子函数内部处理
未必属于接口约定的范畴

2-递归父脚本的情况还是会有的
“递归”严格的来说应该是callback(回调)
比如我在上面提到的错误抛出问题
这是一个典型的需要回调父脚本的情况
因为同一个函数中的同一类错误
在不同的脚本环境中可能需要不同方式的处理
所以把错误转交给父脚本处理是一般性的选择
而此时的%0一般会被定位成子函数的标签名
而为了保证错误抛出后不再会被return
也无法使用类似call :main的形式
所以需要一个指示主函数或者父脚本路径的变量

对于返回的错误代码
还是建议尽量以exit /b number形式
利用系统内置的errorlevel吧
作者: CrLf    时间: 2012-11-13 18:23

本帖最后由 CrLf 于 2012-11-13 18:25 编辑

回复 11# qzwqzw


    十分赞同 qzw 对于多返回参数使用指针指向类数组的观点,一直没想到用在这里,确实是个好办法!不过这是否说明我们也许要另外构造一些专门针对类数组的函数?比如清空数组、数组排序、提取与条件匹配的数组等等?
---------------------------------------------------
    不太明白老兄对于递归的意图和顾虑,在子函数中无论何时都可以通过 %0 获取当前函数名、用 %~f0 获取父脚本完整路径(plp看这里~嘿嘿),莫非兄台担忧的是无法在函数的子函数中回调父函数?
---------------------------------------------------
   其实对于 exit /b 我一直有比较大的成见,因为效率太低了,但无奈的是只有他能在函数中任意改变 errorlevel...执保留意见
作者: plp626    时间: 2012-11-13 23:53

本帖最后由 plp626 于 2012-11-14 01:21 编辑

回复 12# CrLf

我确实忽视了%~f0获取脚步自身路径;
但这里提供一个不太常见的情形:递归,(这也是后续总要面临的棘手问题)
%0, %~f0,总之, 遇到%var%这样预处理获取的值递归调用中,我暂且认为这是批处理的一个缺陷,
我用的方法就是保存变量然后setlocal; 读取的时候用!var!
--------------------------------------------------
对于多值返回,有时候效率会需要考虑,
函数开启setlocal,要想改写父环境变量,唯一的途径就是在endlocal时后才能改写,
带来的问题就是,你无法在开启setlocal的函数内部去改写外部环境变量的值(只能读,不能写),像C语言的指针传出参数用法没戏了;
有时候你不得不需要在函数内部中途改写外部变量,只好endlocal去改写,那么函数内部产生的“局部” 环境变量 也跟着释放了, 这不是我们想要的,
为此,我再三思考 在这个特殊需求的函数中,放弃开启setlocal

我是这个观点偷懒用setlocal, 你就要为效率付出代价。。

我有个大胆的想法,干脆用高级语言的栈空间思想吧, 临时变量函数内部的变量都用栈来维护。。。
------------------------------------
  1. setlocal
  2. set test1=value1
  3. set test2=value2
  4. set test3=value3
  5. endlocal & set ret=%test1% %test2% %test3%
  6. for %%e in (%ret%) do echo %%e
复制代码
确实是个不错的方法,除去考虑效率的问题,在可读性和可维护性上都很好, 下一步就是需要约定一个 特殊变量 前缀的问题,用于维护这类返回值。。
----------------------------------------------

call 脚本中遇到exit/b 退出就一去不返了
exit/b 仅仅用于cmd下的脚本调试,此时已经不是考虑效率的问题了, 子过程返回得用goto:eof
作者: plp626    时间: 2012-11-14 00:41

关于批量复制,分享一些技巧。。
  1. :pset [ [/a] <varname=value>;] ...
  2. setlocal enabledelayedexpansion&set "arg= %*"
  3. endlocal&set %arg:;=&set %&goto:eof
复制代码
http://www.bathome.net/redirect.php?goto=findpost&ptid=4725&pid=76283&fromuid=353
作者: CrLf    时间: 2012-11-14 00:44

回复 13# plp626


    如果要对父函数递归,那么自然有可能出现某个函数调用子函数后再调用父函数甚至父函数的父函数的特殊情况,这样岂不是要像堆栈一样累加保存每一层的函数名?否则如何保证函数互相调用的过程中总是能知道谁是父函数?
    虽说有时确实需要递归,但函数之间的关联太紧密总是违背编程惯例的。题外话,如果非要调用其他函数,那我觉得必须声明与哪些函数具有依赖性。
------------------------------------------------
    学习 htm dom 的过程中我深深地感受到,非必要情况下函数中对全局变量进行修改实在不利于脚本的维护,所以还是 setlocal 和 endlocal 较好,或者用约定的特殊前缀来定义函数内的变量,父脚本避免定义此类变量,仅在函数中使用,退出前还要将其清空以保证变量表是干净的
------------------------------------------------
    goto :eof 和 exit/b 相比,我觉得无论是效率还是功能都完全处于劣势,个人认为,最好的方案还是使用外置函数,一来不需要花费逐行寻找标签的时间,二来运行到文件末尾时自然会结束函数。
作者: plp626    时间: 2012-11-14 00:50

本帖最后由 plp626 于 2012-11-14 01:00 编辑

回复 15# CrLf


    递归是迟早要考虑的,一大堆的脚本,互相调用,掉得多了,迟早会遇到有间接调用自己的时候。。

声明函数调用依赖这会使得问题很复杂, 你会充当人肉解释器的感觉。。。

----------------------
关于全局变量:
函数外部变量的修改在库函数实现中 需求强劲;
作者: CrLf    时间: 2012-11-14 01:01

回复 16# plp626


    只需要按一定的格式声明只能从哪些父函数调用、函数中需要调用到哪些子函数(不考虑间接调用)、引用时是否需要和特定函数按照前后顺序排布就可以了,否则不是很容易在不知情的时候因为勿删具有依赖关系的函数而致错吗?
作者: plp626    时间: 2012-11-14 01:13

a 调 b c;
b 调 d e;
c 调 b d a;

函数依赖关系不是树状的, 你会不自觉地陷入间接调用的情况,你要强制不能递归还得额外做些检测工作, 仅不能递归,我觉得就大大削弱库的功能,

一个的格式声明。。。,那么每个函数都会产生额外的解析语句,这一点你是否考虑过
作者: CrLf    时间: 2012-11-14 01:26

回复 18# plp626


    只需要声明下级函数就够了,不管更细的分支。如果这个声明的格式足够统一,那么完整的拓扑结构其实可以另外写个独立的脚本来分析
作者: qzwqzw    时间: 2012-11-14 08:37

本帖最后由 qzwqzw 于 2012-11-14 17:45 编辑
是否说明我们也许要另外构造一些专门针对类数组的函数?

这个可以有,但未必是必须的,因为大多数场合下,我们只会对这个类数组进行简单的操作

不太明白老兄对于递归的意图和顾虑

这个你成功的说服了我,确实在函数内部定义引用宿主脚本的环境变量不是必须的
这个我是受了过去代码的思维定势了
%~f0的用法我以前见到过,只不过没有留下印象
现在我对它的印象是比较深刻了

exit /b 我一直有比较大的成见,因为效率太低了
call 脚本中遇到exit/b 退出就一去不返了

我认为exit /b可以用在抛出错误的特定场合
这种场合下“退出就一去不返”是需要的特性
而大多数的脚本错误抛出不会太多次
所以效率是不需要着重考虑的问题

goto :eof 和 exit/b 相比,我觉得无论是效率还是功能都完全处于劣势,个人认为,最好的方案还是使用外置函数,一来不需要花费逐行寻找标签的时间,二来运行到文件末尾时自然会结束函数。

goto :eof仍然应该是大多数函数结束自身的代码
一者是编程者的思维惯性
二者是与外置函数需要重新定位文件相比,它的效率未必低多少
况且goto :eof有着与goto :end不同的处理方式
效率未必会差到哪里

有时候你不得不需要在函数内部中途改写外部变量

这种场合我还没有遇到过
一般来说如果在函数内部改写了外部变量
那么至少在函数结束前这个改写都是有效的
只需要在函数结束前用endlocal&set
那么这个改写就持久化了
除非遇到函数内部以类exit的形式非正常结束
这个改写才是无效的
但如果将类exit的形式定义为只有发生错误才能使用
那么不改写反而符合正常的逻辑

干脆用高级语言的栈空间思想吧, 临时变量函数内部的变量都用栈来维护

栈的结构决定了存取时的复杂性
将这种复杂性普遍应用于整个库上是得不偿失的

14楼变量批量复制的实现不错

递归是迟早要考虑的,一大堆的脚本,互相调用,掉得多了,迟早会遇到有间接调用自己的时候

递归是少部分函数需要考虑的事情
另外凡是支持递归的函数都应该自己设置递归终结
我还看不出递归和声明函数调用依赖之间的关系

另外由于递归带来的另一问题是setlocal的层级限制
所以如果是支持递归的函数
建议不使用setlocal
或者在内部另使用一组标签来进行递归
这个与函数内部if...goto...形式的条件循环类似

由此带来另外一个问题
函数内部的标签命名是否需要标准化
以避免出现多个类似:loop/:menu/:end重复标签的情况发生
作者: CrLf    时间: 2012-11-14 14:52

回复 20# qzwqzw


   
另外由于递归带来的另一问题是setlocal的层级限制
所以如果是支持递归的函数
建议不使用setlocal

每次 call 都是独享 32 张表空间的,所以不必担心递归时会超过 setlocal 上限,以前测试时写的一个 内存杀手.bat:
  1. @echo off
  2. %1 start /b cmd /v /c %0 :&exit/b
  3. ::启动一个新的 cmd 打开变量延迟而避免使用 setlocal,并关闭自身
  4. for /f "delims==" %%a in ('set') do set %%a=
  5. set f=for /l %%a in (9 -1)do
  6. %f:9=7% %f:a=b% %f:a=c% %f:a=d% set @%%a%%b%%c%%d=@@@@@@@@@@@@@@@@@@@@@%%a%%b%%c%%d
  7. for /l %%a in (8000 1 8191) do set @%%a=@@@@@@@@@@@@@@@@@@@@@%%a
  8. rem 设置一大堆的变量,总量为 8192*32 字节
  9. :test
  10. set /a f+=1
  11. (set /a f+=31
  12. for /l %%a in (%f% 1 !f!) do (
  13. echo %%a
  14. setlocal
  15. rem 不断 setlocal 开辟新的变量表
  16. ))
  17. pause
  18. call :test
  19. ::每次 call 都拥有独立的内存空间,所以又可以执行 32 个 setlocal...
复制代码

作者: CrLf    时间: 2012-11-14 15:43

本帖最后由 CrLf 于 2012-11-14 16:42 编辑

回复 13# plp626


    我的习惯是中途不改写,到结束脚本时再统一改写,多变量转存也许可临时定义含有 for 嵌套的变量型函数,有构思,回家试。另,昨天没理解老兄说的一去不返,今天才有点明白,莫非exit/b会同时退出所有子函数?还有,现在想来,调用外部函数时,%~f0是否还能获取父脚本名?手机无法测试,求验证
————————————
我构想中对函数依赖的声明是作为固定格式的注释写给使用者参考的,并不需要实际执行
————————————
函数内部的标签若要标准化将面临标签长度的限制,在8个字符长度的局限下如何同时保证可识别性和唯一性?
作者: qzwqzw    时间: 2012-11-14 17:52

本帖最后由 qzwqzw 于 2012-11-14 17:55 编辑

setlocal的层级与call的关系是第一次听说
受教了

exit/b只会退出当前函数的call环境

“调用外部函数”要看用什么方式调用了
据我所知call lib :funciton的方式是无法让%~f0获取父脚本名的

标签长度的问题以前咱们讨论过,你大概忘记了
http://www.bathome.net/redirect. ... 5976&pid=104939

说起注释
我觉的这是一个标准化的重要内容
对于注释的形式、内容都应该有一定的规范
比如所有的函数库都必须通过注释注明
修订版本、更新日期、可运行环境 、遵循的接口标准
所有的函数都必须注明函数的用途、用法、已发现的限制等
作者: CrLf    时间: 2012-11-14 18:43

本帖最后由 CrLf 于 2012-11-14 18:52 编辑

回复 23# qzwqzw


    个人认为对于内嵌在脚本中的函数,所有东西都放进去实在有点冗杂,函数的参数接口和版本即可,具体说明在独立的使用范例脚本中注明较好。
但这样一来,内嵌函数与外置函数岂不是要使用两套标准?
作者: Demon    时间: 2012-11-14 19:32

本帖最后由 Demon 于 2012-11-14 19:34 编辑

虽不明但绝厉。
作者: plp626    时间: 2012-11-14 23:41

本帖最后由 plp626 于 2012-11-14 23:44 编辑

经讨论,用固定环境变量名作为函数返回值这一想法, 优势大于劣势(可维护可读性可扩展性好)

现在讨论下这个返回变量名 名字的约定,我以前用过 $_,$, ##,.... 为了给它起个合理富有意义的名字改了好多次, 现在用的是$_ ,,大家讨论约定个标准。。。
-------------------------------------------
对于入口的参数, 也得有个约定,一个入口参数什么都好说,两个三个也好说,上了10个就不好搞了
而且不少时候,入口参数 兼有传入和传出两重身份

看看大家有什么好的方法。。。使得复杂问题能够简单化解决。。。
作者: CrLf    时间: 2012-11-15 17:15

本帖最后由 CrLf 于 2012-11-16 22:21 编辑

在 endlocal 后保留变量的构思,除了含换行符的变量外应该均可兼容
  1. @echo off&setlocal enabledelayedexpansion
  2. set a=123
  3. set b=abc
  4. set c=@#$
  5. set "getEndlocal=endlocal"
  6. for %%a in (a b c) do (
  7. set "getEndlocal=(for /f "delims=" %%$ in (^!%%a^!)do !getEndlocal!&set "$_%%~a=%%$")"
  8. )
  9. echo 实际命令:
  10. echo !getEndlocal!
  11. echo;
  12. %getEndlocal%
  13. echo 执行后的效果:
  14. set $_
  15. pause
复制代码
如果写成函数就是这样(格式先随便写啦,仅作示例,回头再配合标准改写):
  1. :getEndlocal varName [varName [varName [...]]]     Ver:0.1  By:bathome-Crlf
  2. setlocal enabledelayedexpansion&set "$=endlocal"
  3. for %%a in (%*) do set "$=(for %%$ in (^!%%a^!)do !$!&set "$_%%~a=%%$")"
  4. for /f "delims=" %%a in ("!$!") do endlocal&set "getEndlocal=%%a"
  5. ::getEndlocal函数结束
复制代码

作者: qzwqzw    时间: 2012-11-15 18:23

所谓内嵌和外置函数
你应该是指主程序内和主程序外的函数代码
我在想函数发布时都是“外置”的形式
那么是否需要一个外置变内置的过程
如果只是一两个函数当然手动可以完成
如果超过一定的数量级是否需要用程序来实现

另外外置函数是否也分为两种形式
一是每个函数一个脚本文件
这样就可以不用变内置直接简单的
以 call 函数名.cmd 的形式调用

二是功能相类的函数打包到一个库文件中
如果要调用就得将函数内置化了

返回值变量既要考虑可以快速存取
也要考虑可读性的问题
所以建议用类似$return的形式
另外需要考虑返回值何时被清空的问题
如果由调用方清空似乎有些麻烦
如果不清空那么又存在可能会引用前一个函数的返回值的情况
我能想到的办法
就是在函数内部setlocal的时候
同时将返回值先清空

入口参数没看出有什么约定的必要
如果需要传入且传出
那么由该函数在说明中单独约定就足够了
应该不需要作为整体的接口标准的一部分吧
作者: CrLf    时间: 2012-11-15 22:28

本帖最后由 CrLf 于 2012-11-15 22:32 编辑

对于伪数组的构思,仅作简单示例:
  1. @echo off&setlocal enabledelayedexpansion
  2. call :setAr 1234 @#$ test
  3. echo 第2个元素 ar[1] = %ar[1]%
  4. echo;
  5. echo 最大上标 UB[ar] = %UB[ar]%
  6. echo;
  7. echo 显示“数组”中的所有元素
  8. call :listAr ar
  9. echo;
  10. pause
  11. :setAr
  12. set UB[ar]=-1
  13. for %%a in (%*) do (
  14. set /a UB[ar]+=1
  15. set "ar[!UB[ar]!]=%%a"
  16. )
  17. exit/b
  18. :listAr
  19. for /l %%a in (0 1 !UB[%1]!) do echo;!ar[%%a]!
复制代码
当然可以不定义 UB[arName],但是遍历数组时将很难兼顾通用性和效率
作者: plp626    时间: 2012-11-16 00:11

回复 29# CrLf


有点复杂, 调用应该简洁,可读性好
作者: qzwqzw    时间: 2012-11-16 20:34

我都觉得29楼的代码还是比较清晰简洁的
如果能把UB[ArrayName]换成ArrayName.length可读性更好些

27楼的代码可读性略有些差
没看出为什么不直接赋值而用for嵌套
而且如果v2为类似!v1!的值话
会得到意外的结果

另外思考了下标准所需要遵循的几个原则:
1、被广泛使用的用法不应该轻易改变
2、标准应用后代码的可读性不应该降低
3、无法达成大多数意见的不做约定
作者: CrLf    时间: 2012-11-16 22:32

回复 31# qzwqzw


    我的目的是通用性最大化,常见的方案大思路主要有 set var=%var% 和 set var=%%a 两类,但前者的通用性实在是...
1、
  1. endlocal&set var1=%var1%&set var2=%var2%
  2. rem 若含 " 等将极易致错
复制代码
2、
  1. for %%a in ($ "!var1=!var1!" "var2=!var2!") do (
  2.    if %%a==$ (endlocal) else set "%%a"
  3. )
  4. rem 遇到变量内容含 * 和 ? 则必死无疑
复制代码
3、
  1. set 换行符=^
  2. for /f "delims=" %%a in ("$!换行符!var1=!var1!!换行符!var2=!var2!") do (
  3.    if %%a==$ (endlocal) else set "%%a"
  4. )
  5. rem 个人认为比较好的方案1,但是获取换行符的那三行不容压缩和添字,难保 copy 的时候会不会出岔子(尤其是有些论坛经常会在行末添加空格)
复制代码
4、
  1. for /f "delims=" %%a in ("var1=!var1!") do (
  2.    for /f "delims=" %%b in ("var2=!var2!") do (
  3.       endlocal
  4.       set "%%b"
  5.    )
  6.    set "%%a"
  7. )
  8. rem 个人认为比较好的方案2,实际上我更喜欢方案3,但这个容易看懂,行数也可以压缩得很短
复制代码

作者: CrLf    时间: 2012-11-16 22:39

话说 endlocal 后保留变量面临一个很郁闷的问题,如果用户在调用函数之前已经开启了变量延迟,而需要保留的变量内容中又含 ! 号,如何避免它被解释掉?难道输出到临时文件再 set /p?可是不用 setlocal 又难保变量环境的干净——尤其是函数间互相调用的时候
作者: qzwqzw    时间: 2012-11-21 22:44

回32楼和33楼
感觉为了通用性过多的牺牲了简洁性
而复杂性的提升往往意味着可读性的下降
同时也意味着健壮性的下降

就楼上所讨论的set保存变量来说
虽然它存在种种的不足和缺陷
但它仍然是绝对主流的用例
因为它简洁易懂而且能处理大多数情形
对于某些特殊性的场合
建议调用者区别对待

另外刚从cndos的备份站点中看到了函数库的讨论帖
很老了但仍然有借鉴意义
http://cndos.fam.cx/forum/viewthread.php?tid=38969&fpage=5
作者: Demon    时间: 2012-11-21 23:46

讨论了那么多年都没开始写,符合我党作风。
作者: plp626    时间: 2012-11-22 00:16

回复 33# CrLf


是个问题, 研究下。。
作者: CrLf    时间: 2012-11-22 04:16

实在没办法的话,为了兼顾可读性和兼容性最大化,只好用临时文件了
  1. set>"%tmp%\var.$"&endlocal
  2. for /f "eol==delims=" %%a in ("%tmp%\var.$") do set "%%a"
  3. del "%tmp%\var.$"
复制代码





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