标题: 仿批处理命令call参数列表 调用 多参数的变量型函数 [打印本页]
作者: plp626 时间: 2011-12-20 20:31 标题: 仿批处理命令call参数列表 调用 多参数的变量型函数
最近和zm讨论中说到这个问题,之前的代码丢了,现在从自己盘里找了这个div子过程(例子比较典型)改造之:- @echo off
- call:div 31 2003 1000 ans
- echo 31/2003 的前1000位小数为:
- echo %ans%
- pause&goto:eof
-
- :div <dividend> <divisor> <pre> <ret> //code by plp
- Setlocal Enabledelayedexpansion&set/a b=%2,R=%1%%b*10&set "dc="
- For /l %%z In (1 1 %3)Do set/a d=R/b,R=R%%b*10&set dc=!dc!!d!
- endlocal&set %4=%dc%
复制代码
这是典型的call调用,div子过程共4个参数,
第一个参数为被除数,第二个为除数,第三个为小数的位数(精度),第四个为返回的变量名
调用后子过程没有改变父环境变量值,因为有setlocal 和endlocal 。。。
讨论的问题是,如果用变量型函数像call这样“调用”多参数,代码如何写?- @echo off
- :: 定义_div 变量型函数
- rem 代码。。。。。?
-
- %_div% 31 2003 1000 ans
- echo 31/2003 的前1000位小数为:
- echo %ans%
复制代码
- @echo off
-
- :: 定义_div 变量型函数 ;返回两个整数商的小数部分
- :: <dividend> <divisor> <pre> <ret> //code by plp
- Set "_div=setlocal enabledelayedexpansion&set n=&set argv=&for %%a in (1 2)do if defined argv ((for %%b in (^!argv^!)do set/a n+=1&set #^!n^!=%%b)&set/a b=^!#2^!,R=^!#1^!%%b*10&set dc=&(For /l %%z In (1 1 ^!#3^!)Do set/a d=R/b,R=R%%b*10&set dc=^!dc^!^!d^!)&for /f "tokens=1-2" %%A in (^"^^!#4^^! ^^!dc^^!^^")do endlocal&set %%A=%%B)else set argv="
-
-
- %_div% 31 2003 1000 ans
- echo 31/2003 的前1000位小数为:
- echo %ans%
-
- if "%~0"=="%~f0" set/p=
- goto:eof
- ___END___
-
- 思路大致是
- setlocal&set argv=
- for %%a in (1 2)do if defined argv (
- for 获取argv的变量值用#“数组”存放,#1为第一个参数入口,#2为第二个参数入口。。。
- 你的变量型函数在这里展开。。。。
- endlocal&退出并复制结果给返回变量名
- ) else set argv=
复制代码
对于变量型函数多参数的调用,有简单直接的思路(但要写代码者自己维护变量空间)
大家可参看此贴一楼的测试代码
http://www.bathome.net/viewthread.php?tid=11799
新手对变量型函数不太了解的,可参看变量型函数发源贴:
http://www.bathome.net/thread-5861-1-1.html
作者: CrLf 时间: 2011-12-20 20:52
绝了!
作者: qq2501 时间: 2011-12-20 21:34
看的我云里雾里
作者: CrLf 时间: 2011-12-20 22:05
回复 3# qq2501
%_div% 31 2003 1000 ans 就是用第一次 for 循环将其后跟随的 31 2003 1000 ans 赋值给变量 :,再在第二次循环中引用,通过次序的颠倒,巧妙地在执行函数体之前,取得位于函数体之后的值。
函数中不是有一句 for %%a in (1 1)do if defined : ( ... )else set := 吗?这里的第一个次循环就是用来执行 else 进行取值的,赋值成功后,第二次循环才是执行函数内容
作者: CrLf 时间: 2011-12-21 07:00
本帖最后由 CrLf 于 2011-12-21 07:49 编辑
发一个将普通“格式化”为变量型函数的工具,格式化后的函数,只有用变量延迟直接或二次扩展才能动态地读取变量,而且原有空行、以 : 开头的行均被忽略。
但是思路比较粗糙,对 % 未进行成对判断,对开启变量延迟后含有 ! 的命令未做充分的二次转义处理(难度太大,等于要解析语法),并且不支持扩展参数,参数外的双引号必须被去除(其实也不是必须,但是条件太苛刻)。
合并行.bat :- @echo off
- echo %*
- set "g=&"
- set "code=setlocal enabledelayedexpansion&set n=&set :=&for %%%%a in (1 1) do if defined : ((for %%%%b in (!:!) do set/a n+=1&set #!n!=%%%%b"
- for %%z in (%*) do (
- for /f "usebackeol=:tokens=*" %%b in ("%%~z") do (
- set str=%%b
- setlocal enabledelayedexpansion
- if !str:~^,1! neq ^) (set "str=(!str!") else set g=
- if !code:~-1! neq ^( (set "code=!code!)") else set g=
- set code=!code!!g!!str!
- for /f "delims=" %%c in ("!code!") do endlocal&set code=%%c
- )
- call :Test %%z
- )
- exit
- :Test
- set out=&setlocal enabledelayedexpansion
- set "code=!code!)) else set :="
- set b=!code!
- for /l %%a in (1 1 9) do set code=!code:%%%%a=$#%%a$!
- for /l %%a in (0 1 8000) do (
- set /a "n-=^!^!n"
- if !code:~%%a^,4!==$ (set "out=!out!^!"&set code=!code:~3!) else set out=!out!!code:~%%a,1!
- if !code:~%%a!.==. echo 合并后:!out!&echo !out!>1.cmd&endlocal&call 转义.bat 1.cmd "_%~n1"&pause&exit /b
- )
复制代码
转义.bat :- @echo off&setlocal enabledelayedexpansion&set hh=^
-
-
- for %%a in ("!hh!") do (
- endlocal
- set out=
- set tmp=
- for /f "usebackdelims=" %%b in (%1) do set hs=%%b
- setlocal enabledelayedexpansion
- for /f "eol=;delims=" %%b in ("!hs:"^=%%~a"!") do (
- endlocal&set str=%%b&set /a n=!n
- setlocal enabledelayedexpansion
- if !n!==0 set "str=!str:^=^^!"
- if !n%%b neq n%%b (
- for /l %%c in (8101 -100 1) do if "!str:~%%c!"=="" set len=%%c
- for /l %%c in (0 1 !len!) do if !str:~%%c^,1!==^^! (
- if !n!==0 (set "tmp=!tmp!^^^^^^^!") else set "tmp=!tmp!^^^!"
- ) else set "tmp=!tmp!!str:~%%c,1!"
- set str=!tmp!
- )
- if !n!==0 for %%c in (^( ^) ^& ^| ^< ^>) do set "str=!str:%%c=^%%c!"
- for /f "eol=:delims=" %%c in ("!out!!str!") do endlocal&set out=%%c
- )
- )
- setlocal enabledelayedexpansion
- echo 转义后:!out!&echo set "%~2=!out!">>2.cmd
- pause
复制代码
样本:- :strlen <stringVarName> [retvar]
- :: 思路: 二分回溯联合查表法
- :: 说明: 所求字符串大小范围 0K ~ 8K;
- :: stringVarName ---- 存放字符串的变量名
- :: retvar ---- 接收字符长度的变量名
- :: 原帖:http://www.dostips.com/forum/viewtopic.php?f=3&t=1429
-
- setlocal enabledelayedexpansion
- set $=%1#
- set N=&for %%z in (4096 2048 1024 512 256 128 64 32 16)do if !$:~%%z!. NEQ . set/aN+=%%z&set $=!$:~%%z!
- set $=!$!fedcba9876543210&set/aN+=0x!$:~16,1!
- for /f "delims=" %%a in ("%2=!n!")do endlocal&set %%a
复制代码
拖动样本到 合并.bat 上的处理结果:- set "_%%:=setlocal enabledelayedexpansion&set n=&set :=&for %%a in (1 1)do if defined : ((for %%b in (^!:^!)do set/a n+=1&set #^!n^!=%%b)&(setlocal enabledelayedexpansion)&(set $=^!#1^!#)&(set N=&for %%z in (4096 2048 1024 512 256 128 64 32 16)do if ^!$:~%%z^!. NEQ . set/aN+=%%z&set $=^!$:~%%z^!)&(set $=^!$^!fedcba9876543210&set/aN+=0x^!$:~16,1^!)&&(for /f "delims=" %%a in ("^^^!#2^^^!=^^^!n^^^!")do endlocal&set %%a))else set :="
复制代码
作者: CrLf 时间: 2011-12-21 08:07
DIY 一个定义全局函数的模板,部分靠先前的代码转换,部分靠手工(搞死人了...),变量型函数的全局声明和调用:- @echo off
- call :dim strlen checkdate count
- setlocal enabledelayedexpansion
- %_strlen% basfdglksdfjlsdkj n
- echo "test测试Hello World" 中共有 %n% 个字符
- echo;
- echo 检查日期是否正确
- %_checkdate% 2011 2 29
- echo 2011年 2月 29日的检查结果如上
- echo;
- %_count% a ahahahahahahhhahahhhaha n1
- %_count% h ahahahahahahhhahahhhaha n2
- echo ahahahahahahhhahahhhaha 中有 %n1% 个 a、 %n2% 个 h
- pause&exit
- :dim
- for %%: in (%*)do (
- if /i %%:==strlen set "_%%:=setlocal enabledelayedexpansion&set n=&set :=&for %%a in (1 1)do if defined : ((for %%b in (^!:^!)do set/a n+=1&set #^!n^!=%%b)&setlocal enabledelayedexpansion&set $=^!#1^!#&(set N=&for %%z in (4096 2048 1024 512 256 128 64 32 16)do if ^!$:~%%z^!. NEQ . set/aN+=%%z&set $=^!$:~%%z^!)&set $=^!$^!fedcba9876543210&set/aN+=0x^!$:~16,1^!&&for /f "delims=" %%a in ("^^!#2^^!=^^!n^^!")do endlocal&set %%a)else set :="
- if /i %%:==checkdate set "_%%:=setlocal enabledelayedexpansion&set n=&set :=&for %%a in (1 1)do if defined : ((for %%b in (^!:^!)do set/a n+=1&set #^!n^!=%%b)&setlocal disabledelayedexpansion&set/a"y=#1,m=#2,d=#3,test=^^!^(y%%4^|^^!^(y%%100^)*^^!^^!^(y%%400^)^)*^^!^(m^^^^2^)+^(m+m/8^)%%2-2*^^!^(m^^^^2^)+30,0/^(test/d*^^!^(m/13^)^)"2>nul&&echo Right||echo Wrong)else set :="
- if /i %%:==count set "_%%:=setlocal enabledelayedexpansion&set n=&set :=&for %%a in (1 1)do if defined : ((for %%b in (^!:^!)do set/a n+=1&set #^!n^!=%%b)&setlocal enabledelayedexpansion&set key=:^!#1^!&set str=^!#2^!&set ret=^!#3^!&(for %%a in ("^^!_hh^^!")do for /f "delims=" %%b in ("^^!key^^!")do for /f "delims=" %%c in ("[^^!str%%b=%%~a]^^!")do set /a n+=1)&for /f "delims=" %%a in ("^^!ret^^!=^^!n^^!")do endlocal&set %%a)else set :="&set _hh=^
-
-
- )
复制代码
作者: netbenton 时间: 2011-12-21 08:33
本帖最后由 netbenton 于 2011-12-21 10:06 编辑
晕倒,我发错地方了,当灌水了。
研究了一下,真的不错
作者: plp626 时间: 2011-12-21 09:40
本帖最后由 plp626 于 2011-12-21 10:05 编辑
回复 7# netbenton
我这是拾人牙慧;
话说很少见netben兄发帖哈,
---------
忽然觉得此帖合并到兄的那个发源贴后有利于坛友讨论,和阅读。
申请合并。
作者: netbenton 时间: 2011-12-21 11:36
不错呀~~
这样可以增加了通用性和可读性
::水话::
没有那么多时间玩BAT了,看到强铁,不得不顶~~
::水话::
作者: Demon 时间: 2011-12-21 15:45
2011年国际Batch混乱代码大赛第一名。
作者: CrLf 时间: 2011-12-21 16:22
回复 3# qq2501
回复 10# Demon
原文跨度太大,直接从普通函数成型为改进的变量型函数了,缺乏过渡确实不太容易理解,我还是“翻译”一下吧,希望有助于大家的理解。
当原文中声明了 _div 这个变量型函数之后,运行以下代码:复制代码
的实际效果等同于执行了以下代码:- setlocal enabledelayedexpansion
- set n=
- set argv=
- for %%a in (1 2) do (
- if defined argv (
- rem 因为此时 argv 为空,所以先执行的是 else,即 set argv= 31 2003 1000 ans
- for %%b in (!argv!)do set/a n+=1&set #!n!=%%b
- rem 第 2 步,将获取的参数分别设为 #1、#2 一直到 #N
- rem 在例子中,共有四个参数:#1=31,#2=2003,#3=1000,#4=ans
- set/a b=!#2!,R=!#1!%%b*10&set dc=
- For /l %%z In (1 1 !#3!)Do set/a d=R/b,R=R%%b*10&set dc=!dc!!d!
- rem 第 3 步,调用函数主体,针对参数进行操作
- for /f "tokens=1-2" %%A in ("!#4! !dc!")do endlocal&set %%A=%%B
- rem 第 4 步,结束上一个 setlocal 并利用 for 保留变量
- )else (
- set argv= 31 2003 1000 ans
- rem 第 1 步:将参数赋值
- )
- )
- rem 最终结果存储在 ans 变量中。
复制代码
核心思想就是利用 for+if 先执行后面的代码(进行赋值),再回过头来执行前面的代码(具体操作)。
这样解释不知还有难理解的地方吗?
作者: cjiabing 时间: 2011-12-22 22:56
天马行空,眼花缭乱。我看看看!
作者: powerbat 时间: 2011-12-23 23:20
偶也这么用过,嘻嘻。但个人对批处理函数不怎么感兴趣(如果bat稍微复杂,我往往用其他脚本)。
依照C语言,这种形式叫“宏函数”比较合适。
作者: wankoilz 时间: 2012-1-7 09:42
这个从后面获得参数的方法确实巧妙,高!
作者: garyng 时间: 2012-6-11 11:45
这方法很巧妙!
作者: 爱小宝儿 时间: 2012-6-18 16:59
呵呵,谢谢楼主了~~~~~~~~~~~
作者: a415987611 时间: 2012-7-14 19:39
函数什么的,总感觉会影响效率,
函数内部多出了for、if、set,又用setlocal enabledelayedexpansion、endlocal
有时还是宁愿复制、粘贴
作者: zh_1452 时间: 2014-6-21 07:09
- echo 这个内容太长了一时半伙看不懂拿回家好好研究一下
复制代码
欢迎光临 批处理之家 (http://bathome.net./) |
Powered by Discuz! 7.2 |