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

【项目1】批处理函数库调用接口的约定

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

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

接口约定,涉及到全局变量约定(对使用者不可见); 讨论的问题很多。。。
SOS --- >> lllsoslll@163.com

TOP

只要注释写得好,问题不大……

TOP

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

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

返回变量名为第一个实参名

TOP

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

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

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


所有C标准库函数都会设置errno?

TOP

回复 5# Demon


我没这么说,
    你是不是理解错了?

TOP

回复  Demon


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



    算我理解错了。

TOP

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

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

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

TOP

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

回复 8# qzwqzw


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

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

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

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

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

TOP

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

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

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


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

程序自身路径:
这是个棘手的问题,如果多个函数调用,就得用setlocal来解决, 所以还是看大家对setlocal的讨论

TOP

本帖最后由 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吧
天的白色影子

TOP

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

回复 11# qzwqzw


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

TOP

本帖最后由 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

TOP

关于批量复制,分享一些技巧。。
  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

TOP

回复 13# plp626


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

TOP

返回列表