|
无奈何
荣誉版主
积分 1338
发帖 356
注册 2005-7-15
状态 离线
|
|
2005-11-17 16:42 |
|
|
willsort
元老会员
Batchinger
积分 4432
发帖 1512
注册 2002-10-18
状态 离线
|
『第
2 楼』:
Re 无奈何:
很有见地的一篇主题,根据其讨论的内容和深度,相信可以归入(開發室) ,只是彼处对此能会心一笑的人太少了,还是暂时先放在这里讨论吧。
动态参数的支持,我在学习批处理之初便曾有接触,只是当时未及深入,疏漏自然难免。后来再编写那个文本遍历程序的命令行增强版(VisitCE)时,曾试图对此进行更深入的探索,但是一入其中,便觉如同幽魂迷宫,错综复杂,需要考虑和斟酌的东西太多了。此时,才对DOS命令行的灵活性有了另一番深入的体会。
关于多个动态参数的支持,我建议使用 :loop 和 goto loop 的循环对参数分析,这样多少可以避免程序陷入 if else 的迷魂阵中,这也是其他高级语言的代码处理参数的通常做法。对所有的参数都一视同仁,首先判断是否空,其次判断其所属类别,再次判断其是否有效,最后判断其是否可以保存(即是否已经保存或其他情形)。这样即使处理任意多个动态参数,也不需要对代码有太大的更改。
另外,将参数分为目录和数字的分类法,必然会导致参数的二义性问题,从而导致程序分析的复杂化,许多高级语言规定变量名不允许以数字起始,便基于此项原因(反例是DOS的环境变量)。想要避免它,就需要重新划分参数的类别,你所说的全路径便是一种折中的办法。
对于判断参数是否数字,如果允许使用外部命令,那么可以考虑 finstr 的正则表达式过滤。
最后,给出一个题目,大家讨论一下:如何在 MS-DOS下 实现相同的需求?
|
※ Batchinger 致 Bat Fans:请访问 [讨论]批处理编程的异类 ,欢迎交流与共享批处理编程心得! |
|
2005-11-17 18:31 |
|
|
无奈何
荣誉版主
积分 1338
发帖 356
注册 2005-7-15
状态 离线
|
『第
3 楼』:
和之者寡,如我猜想一样这果然是一个淡帖。本想续个下篇完结这个问题的讨论,也算作是学习批处理半年多来的总结,可我实在无力、无心完成了。
谢谢 willsort 兄的肯定与鼓励,你的建议让我获益颇多。
上篇文章中留了个尾巴,实在不爽,可我又无心再续。再贴一段批处理程序,算作上篇遗留问题的处理吧。这是我的某个程序的一段,部分更改后做成个演示程序。这段代码是我几次补充修改后的结果,可能有些乱,我不作解释了,因为对此感兴趣的朋友应该都能看明白。
:: demo2.cmd 批处理参数处理演示 2 2000、XP ↑
:: by:无奈何 email:wunaihe@gmail.com 2005.11.25
@echo off
rem xss [/I 输入路径] [/F 显示类型|/V 过滤类型] [/L 输出记录文件] [/O 输出路径]
setlocal
set /a n=0,m=1
set /a I=0,F=0,V=0,L=0,O=0
:loop
set "_temp=%~1"
if "%_temp%" == "" goto :star
if "%m%" == "1" (
echo "%_temp%" |findstr/i /r "\</I\> \</F\> \</V\> \</L\> \</O\>" 2>nul 1>&2
if errorlevel 1 echo 非识别参数 “%_temp%” &goto :EOF
)
set /a n=n+1,m=m+1
if /i "%_temp%" == "/I" set /a I=1,F=0,V=0,L=0,O=0,n=1
if /i "%_temp%" == "/F" set /a I=0,F=1,V=0,L=0,O=0,n=1
if /i "%_temp%" == "/V" set /a I=0,F=0,V=1,L=0,O=0,n=1
if /i "%_temp%" == "/L" set /a I=0,F=0,V=0,L=1,O=0,n=1
if /i "%_temp%" == "/O" set /a I=0,F=0,V=0,L=0,O=1,n=1
if "%n%" == "2" (
if "%I%" == "1" set "directory_i=%_temp%" &set m=1
if "%F%" == "1" set "filter_f=%_temp%" &set m=1
if "%V%" == "1" set "filter_v=%_temp%" &set m=1
if "%L%" == "1" set "log=%_temp%" &set m=1
if "%O%" == "1" set "directory_o=%_temp%" &set m=1
)
shift
goto loop
:star
echo.directory_i %directory_i%
echo.filter_f %filter_f%
echo.filter_v %filter_v%
echo.log %log%
echo.directory_o %directory_o%
goto :EOF [ Last edited by 无奈何 on 2005-11-27 at 15:02 ]
|
☆开始\运行 (WIN+R)☆
%ComSpec% /cset,=何奈无── 。何奈可无是原,事奈无做人奈无&for,/l,%i,in,(22,-1,0)do,@call,set/p= %,:~%i,1%<nul&ping/n 1 127.1>nul
|
|
2005-11-25 23:42 |
|
|
willsort
元老会员
Batchinger
积分 4432
发帖 1512
注册 2002-10-18
状态 离线
|
『第
4 楼』:
Re 无奈何:
知音难觅,古之良训,兄因此感怀可见也是性情中人。
以下是我的代码,主要针对于 MSDOS/Win9x 环境,在 NT’s 应该也没有什么大问题,当然未经过严格测试,我不能做保证的。
代码中没有做参数有效性的验证,也没有实现遇到重复开关的验证,不过在实际应用中,可以根据各类型参数的实际情况,比较简单地插入验证代码到相应的模块中。
兄的代码中,有以下几点疑问:
1、 set "_temp=%~1" ,我猜测将参数保存至变量的目的在于,剔除参数中的双引号,这是 %~1 的作用,但是我不明白为何将将变量名加参数都用引号括起。
2、变量 m n,我猜测它们的作用是标示参数的奇偶性,即表明参数是开关还是变量值,但是否可用一个变量的真假来标示?我的方案中因为是用双shift避免了开关和变量值的选择的,这样可能会引起 prog /i /o 这样的识别问题,但是如果在 SwitchI 中加入有效性验证,那么也可以避免。
3、set /a I=1,F=0,V=0,L=0,O=0,n=1 ,这样的设置会如何识别 prog /i input /o output /i input2 ?
:: ArgParse.bat - Parser of command line arguments
:: Will Sort - 2005-11-26 - WinXP_CMD/MSDOS7.10/MSDOS6.22
@echo off
:Init
for %%e in (directory_i directory_o filter_f filter_v log) do set %%e=
:ParseLoop
if "%1"=="" goto Start
for %%s in (i I f F v V l L o O) do if "%1"=="/%%s" goto Switch%%s
echo Error: Invalid switch '%1'!
goto Quit
:SwitchI
if "%directory_i%"=="" set directory_i=%2
goto NextArg
:SwitchO
if "%directory_o%"=="" set directory_o=%2
goto NextArg
:SwitchF
if "%filter_f%"=="" set filter_f=%2
goto NextArg
:SwitchV
if "%filter_v%"=="" set filter_v=%2
goto NextArg
:SwitchL
if "%log%"=="" set log=%2
goto NextArg
:NextArg
shift
shift
goto ParseLoop
:Start
echo.directory_i %directory_i%
echo.directory_o %directory_o%
echo.filter_f %filter_f%
echo.filter_v %filter_v%
echo.log %log%
:Quit
for %%e in (directory_i directory_o filter_f filter_v log) do set %%e=
:end [ Last edited by willsort on 2005-11-26 at 19:35 ]
|
※ Batchinger 致 Bat Fans:请访问 [讨论]批处理编程的异类 ,欢迎交流与共享批处理编程心得! |
|
2005-11-26 19:32 |
|
|
无奈何
荣誉版主
积分 1338
发帖 356
注册 2005-7-15
状态 离线
|
『第
5 楼』:
to: willsort
高山流水觅知音,能有兄的回复倍感欣喜。
1、“%~1”如你所说确实是想剔除双引号,如果一个变量的值可能有双引号也可能没有,会造成一些调用的不便。如果一个变量“ABC”值含有空格、分号这样的字符时调用标签函数,call :test %ABC% 势必会将 ABC 拆分为多个参数,所以会这样用 call :test "%ABC%" 这样就会造成传递过去的参数会有双引号包围和两个双引号包围的情况。在我应用中还碰到其他的一些问题,一时想不起来了。我也就养成习惯统一去除双引号了。
还有用双引号将变量名和值括起来有几点好处,一是这样的话可以使用含有空格的变量名如: set "A B=A-B",二是将变量赋值时防止尾部会有不小心键入的空格如:set "A=" 。这些小技巧可以避免一些不必要的麻烦。
2、变量 m 与 n 的作用是标示是否是参数标示(开关)和参数变量,但不是以奇偶性来判断的,我最初也是用一个变量采用取余的方法按奇偶判断,出现了你提及的连续出现两个有效参数标示时其后部分全部出错的情况。n 的作用是会确定有效参数标示后紧跟的一个非有效参数标示为参数变量。m 的作用刚好相反,它会认定参数变量其后紧跟的是一个参数标示。
3、设置 I,F,V,L,O 这几个标志变量的作用就是想让程序能够正确接收无序的并且个数不固定的参数。当一个有效参数标示到来时,对应的标志变量置 1 ,其他标志变量置 0 ;进入下一次循环,当一个有效参数变量到来时,根据标志变量是否为 1 ,就可以判断上次接收到的是哪一个参数标示,然后设置对应的变量值。碰到你提及的有两个重复参数的时候,后一个的值会替代前一次接收的值,也就是以最后输入为准。还有就是当想要标志变量只作开关而无参数变量时,可以直接在最初的判断处添加赋值语句解决这种问题。
我仔细看了兄的示例,代码确实优秀,流畅程度远远超过了我的拙作。如果再多一层判断完善单步走与双步走的两种分支,也可以很好的解决标志变量只作开关的情况。
[ Last edited by 无奈何 on 2005-11-27 at 13:08 ]
|
☆开始\运行 (WIN+R)☆
%ComSpec% /cset,=何奈无── 。何奈可无是原,事奈无做人奈无&for,/l,%i,in,(22,-1,0)do,@call,set/p= %,:~%i,1%<nul&ping/n 1 127.1>nul
|
|
2005-11-26 23:35 |
|
|
willsort
元老会员
Batchinger
积分 4432
发帖 1512
注册 2002-10-18
状态 离线
|
『第
6 楼』:
Re 无奈何:
很抱歉!因为各种主观和客观原因,我的回复被迫推迟了。
1、兄对 set "_temp=%~1" 的解释确实有理,尤其是防止尾部空格的提示;因为我长期在 MSDOS/Win9x 下编写批处理代码,对带空格参数和带引号参数接触机会不多,所以考虑尚未周全。
经过测试,发现 ntcmd.set 对命令行中的引号处理很周到,对 set _temp=This is "test string" ! 或者 set "_temp=This is "test string" !" 等情况都能恰当的处理;但是在 MSDOS / Win9x 下就会出现很多问题。另外,无论是在 MSDOS / 9X_COMMAND 下,还是在 XP_CMD 下,set var name=value 的用法都是有效的。
2、对 m n 的作用我已基本理解,双变量分别标示开关项和非开关项确实是必要的。
3、起初,我认为你这样的设置会允许开关的重复设定,而这通常是错误的用法,而你在主楼中提到的第6/7组合也被你认定是“不期望的”。不过后来我想到在某些场合中可能会需要这样的用法,这我曾在类 unix 的命令行中和一些复杂的批处理中见到过,所以也就默认了它的存在。
4、关于我的代码,最初设计这个程序时,我是将 prog /i /o output 和 prog /i input /i input2 都列为错误参数用法的。现在想来, prog /i /o output 的情形还是很常用的,尤其是某些参数只是作为启闭某项功能特性的开关(Switch)时,所以将它按照你所说的单双步分法作了简单的改进。
此外,我的程序也允许 prog /i input1 /i input2 这种情形,不过与你的代码正相反,它只取第一个设定值;如你所言,想要改成取最后一个也很简单,只需要将判断是否已设置的 if 条件删除即可;而如果要禁止这种情形,也只需要将条件取反后进行错误处理即可。但要处理未必含有设定值的开关时就比较麻烦,需要对参数进行检查了。下面是新的代码,在线完成,未经测试!
:: ArgParse.bat - V2 - Parser of command line arguments
:: Will Sort - 2005-11-30 - WinXP_CMD/MSDOS7.10/MSDOS6.22
@echo off
:Init
for %%e in (directory_i directory_o filter_f filter_v log) do set %%e=
:ParseLoop
if "%1"=="" goto Start
for %%s in (l L i I o O f F v V) do if "%1"=="/%%s" goto Switch%%s
echo Error: Invalid switch '%1'!
goto Quit
:: 一个有设定值的开关,如果重复设定将取最后一个
:SwitchI
set directory_i=%2
goto Next2Arg
:: 一个有设定值的开关,如果重复设定将取第一个
:SwitchO
if "%directory_o%"=="" set directory_o=%2
goto Next2Arg
:: 一个有设定值的开关,如果重复设定将抛出错误后终止程序
:SwitchF
if not "%filter_f%"=="" echo Error: Replicative switch '%1'!
if not "%filter_f%"=="" goto Quit
set filter_f=%2
goto Next2Arg
:: 一个可能有设定值的开关,如果未在其后设定值,将取默认值
:SwitchV
set filter_v=default
for %%s in (l L i I o O f F v V) do if "%2"=="/%%s" goto NextArg
if not "%2"=="" set filter_v=%2
goto Next2Arg
:: 一个没有设定值的开关,只有启闭功能,重复设定将反复取反
:SwitchL
if "%log%"=="" set _log=on
if "%log%"=="on" set _log=off
if "%log%"=="off" set _log=on
set log=%_log%
set _log=
goto NextArg
:Next2Arg
shift
:NextArg
shift
goto ParseLoop
:Start
echo.directory_i %directory_i%
echo.directory_o %directory_o%
echo.filter_f %filter_f%
echo.filter_v %filter_v%
echo.log %log%
:Quit
for %%e in (directory_i directory_o filter_f filter_v log) do set %%e=
:end [ Last edited by willsort on 2005-11-30 at 12:24 ]
|
※ Batchinger 致 Bat Fans:请访问 [讨论]批处理编程的异类 ,欢迎交流与共享批处理编程心得! |
|
2005-11-29 17:34 |
|
|
无奈何
荣誉版主
积分 1338
发帖 356
注册 2005-7-15
状态 离线
|
『第
7 楼』:
to:willsort
事实上 prog /i input1 /i input2 这种情况取前者与后者都是合理的,只是策略问题,完全可以怎么方便怎么来。
兄的下面一段代码,按你的功能提示来看好像漏掉了一种可能情况。
Quote: | :SwitchV
for %%s in (l L i I o O f F v V) do if "%2"=="/%%s" set filter_v=default
for %%s in (l L i I o O f F v V) do if "%2"=="/%%s" goto NextArg
set filter_v=%2
goto Next2Arg |
|
测试:prog /i input1 /v 发现并未按设想的赋值。可以加一条语句如下:
:SwitchV
for %%s in (l L i I o O f F v V) do if "%2"=="/%%s" set filter_v=default
for %%s in (l L i I o O f F v V) do if "%2"=="/%%s" goto NextArg
set filter_v=%2
if "%2"=="" set filter_v=default
goto Next2Arg 还有一个疑问,相同条件的语句兄为什么不作合并。如:
for %%s in (l L i I o O f F v V) do (
if "%2"=="/%%s" set filter_v=default
if "%2"=="/%%s" goto NextArg
) 这样可以减少一次循环,或者更简洁的将 if 句也合并,是兼容性的考虑吗?我不了解 DOS 下的问题。
|
☆开始\运行 (WIN+R)☆
%ComSpec% /cset,=何奈无── 。何奈可无是原,事奈无做人奈无&for,/l,%i,in,(22,-1,0)do,@call,set/p= %,:~%i,1%<nul&ping/n 1 127.1>nul
|
|
2005-11-29 21:21 |
|
|
willsort
元老会员
Batchinger
积分 4432
发帖 1512
注册 2002-10-18
状态 离线
|
『第
8 楼』:
Re 无奈何:
1、prog /i input1 /v 确实是个问题,你的修改是必要的;而且真正要应用的时候,还需要防止 /x /y 或其他无效的开关情形,此时进行有效性验证就是必须的了。
2、两句同样的 for + if 确实是出于兼容性考虑,在 MSDOS / Windows 下,是不支持语句块的。虽然通过特殊的方法可以使两句合并,但是这里并不采用,一方面是兼容性的考虑,另一方面是可阅读性的考虑。
3、prog /i input1 /i input2 这种情况,取前者与后者适合于不同的应用场合。
比如在某些场合,我们可能会使用变量来保存带参数的命令行,然后在以后的某个环境下引用它,此时我们可能需要改变某个开关或参数,却出于某些原因无法修改变量,此时就需要取后者的策略,这样我们可以在引用变量的后面加上新的开关或参数来改变程序的运行状态。
至于取前者的策略则比较多见,但一般没有特别的目的性。但有一重特殊的情况是与上面正相反,我们可能会使用变量保存命令行的部分参数,然后在以后的某个环境下引用它,此时我们可能需要固定某个开关或参数,却出于某些原因无法修改变量,此时需要取前者的策略。或者某些程序会以各种形式预置一些的常用的命令行用法,而且不欲用户有意或无意的修改这些用法,则也需要取前者的策略。这类似于某些 unix 类程序中的 -- 命令行终止参数。
另外,刚注意到你的新签名,确实富有创意和技巧,只是文辞似乎略显伤感了些,恐怕会给论坛的新人带来负面的影响吧。玩笑而已
[ Last edited by willsort on 2005-11-29 at 22:26 ]
|
※ Batchinger 致 Bat Fans:请访问 [讨论]批处理编程的异类 ,欢迎交流与共享批处理编程心得! |
|
2005-11-29 22:19 |
|
|
无奈何
荣誉版主
积分 1338
发帖 356
注册 2005-7-15
状态 离线
|
『第
9 楼』:
哈哈,以后就用你的这个构架了,给个授权吧!
我的签名确实花了一些心力,语句我尽量的精简,想让其保持功能的前提下拥有最少字符,这是我目前能做到的最好情况(%ComSpec% 替换为 cmd 不算),当然也就没有了可读性,故弄玄虚增加点神秘感。再者,无奈何不是一种持久的人生态度也不是现状的反映,可以理解为对种种不能为不可为之事的解嘲、劝抚,或于己事或于人事,多一些自抚调整少一些空劳抱怨。映于己映于人,也有太多的体现。伤感是肯定的,也显低调,但希望的不是低沉。我最初的词句是终是无可奈何,发觉不是我的本意,悄悄改了一字。
|
☆开始\运行 (WIN+R)☆
%ComSpec% /cset,=何奈无── 。何奈可无是原,事奈无做人奈无&for,/l,%i,in,(22,-1,0)do,@call,set/p= %,:~%i,1%<nul&ping/n 1 127.1>nul
|
|
2005-11-30 00:19 |
|
|
willsort
元老会员
Batchinger
积分 4432
发帖 1512
注册 2002-10-18
状态 离线
|
『第
10 楼』:
Re 无奈何:
我的代码既没有声明 Copyright ,也没有附加任何 License ,所以也就不存在授权的问题,你当然可以自由的使用它。对网络自由共享的精神我是很欣赏的。
但是,一个好的框架或代码是需要经过反复测试和不断改进后才能正式应用的,而我的这个代码只是在线编写的一段代码,很可能存在许多潜在的问题和不完善。
昨夜入眠之前,就突然醒起一个地方可再做一下改进,就是你提到的双 for 语句处,我将前一个 for 省略了。代码不再重贴,只是编辑了6楼的原 V2 版代码。
|
※ Batchinger 致 Bat Fans:请访问 [讨论]批处理编程的异类 ,欢迎交流与共享批处理编程心得! |
|
2005-11-30 10:35 |
|
|
maya0su
中级用户
积分 241
发帖 131
注册 2005-9-28
状态 离线
|
『第
11 楼』:
苦者律己,行者无奈……
人生本就是一件痛苦的事,可是生来为人,我们不能沉沦,只有振作,吾爱吾爱之人……奈何花凋零……
无奈何兄的贴子我仔细反复看了看,最终的结果是我不参与讨论,因为我是今天才发现的这个帖子……而我的能力也非常有限,对于你提出的问题只能再去研究……而willsort兄的回贴确实精彩,你们在问与答之间完成了最初所设想,可喜可贺……弟我只能替你们高兴!
网络的最初设想就是自由和共享,可是现在网络成了人圈钱的工具,实在让我辈心寒,本着自由和共享的精神我谨希望以我们的微薄之力来延续这重种共享和自由,无奈何兄的签名我测了测,很有意思!
试出一个题目:能不能写个批处理程序,用一个字一个字的显示方式,显示出一段话呢?而且是批处理,双击后在窗口显示这短话,完成后屏幕暂停!
仅是想和兄们做个游戏,关注不关注无所谓……博君一笑而!
|
房东说:这娃是个好孩子! |
|
2006-1-16 23:43 |
|
|
mrhjzhang
初级用户
积分 100
发帖 39
注册 2005-10-27
状态 离线
|
|
2006-1-19 16:45 |
|
|
220110
荣誉版主
积分 718
发帖 313
注册 2005-9-26
状态 离线
|
『第
13 楼』:
Quote: | 试出一个题目:能不能写个批处理程序,用一个字一个字的显示方式,显示出一段话呢?而且是批处理,双击后在窗口显示这短话,完成后屏幕暂停! |
|
Re maya0su:
参见无奈何的个人签名。
但其代码真让人晕。呵
希望无奈何朋友能整理出一个易看点的格式。
[ Last edited by 220110 on 2006-2-4 at 16:54 ]
|
|
2006-2-2 10:26 |
|
|
zah98
新手上路
积分 9
发帖 4
注册 2006-10-22
状态 离线
|
|
2006-10-22 11:40 |
|
|
gxfc
新手上路
积分 17
发帖 11
注册 2006-10-22
状态 离线
|
|
2006-10-22 13:27 |
|