Board logo

标题: [讨论]批处理编程的异类 [打印本页]

作者: willsort     时间: 2004-6-20 00:00    标题: [讨论]批处理编程的异类



[讨论]批处理编程的异类



  本主题的目的是针对[分享]批处理编程的异类http://www.cn-dos.net/forum/viewthread.php?tid=8905)中所涉及到的各类技术问题进行讨论和答疑。

  本人欢迎大家就以下话题提出自己的意见和建议。

  1,对此主题中涉及到的技术和技巧的质疑;
  2,对此主题中涉及到的技术和技巧的改进;
  3,此主题未涉及,但是在批处理编程领域很有挑战性的主题;

  同时,为了保证版面的整洁和秩序,也因为本人时间和兴趣的限制,对于以下的各类话题,请各位避免提出,本人无法保证对此给予任何答复。

  1,仅仅称赞或者毫无内容的话题;
  2,与批处理编程主题毫无关联的话题;
  3,要求提供批处理编程资料的话题;
  4,要求提供每句详解的话题;

  希望它能对各位(包括我本人)对批处理编程的认识加深和水平提高有所帮助,也希望大家能对它的秩序维护做出自己的努力。


[ Last edited by willsort on 2005-10-13 at 13:03 ]
作者: willsort     时间: 2005-1-17 00:00    标题: FDISK的命令行参数

FDISK的命令行参数



    FDISK仍然是大家很常用的分区工具,他支持命令行操作也不再是是一个“未公开的秘密”,早在99年就有人发现了FDISK的一些特殊参数,并将这些应用到了批处理文件中,自动分区似乎并不太遥远。然而,仍然有很多的问题需要解决,硬盘大小的确定仍然无法单独依靠fdisk来实现。

    虽然有了gdisk等第三方工具,但是能用DOS自身命令完成相应功能,仍然有很大的应用前景。

    下面是我翻译的国外的关于FDISK命令行参数的资料,国内也有类似的文档,但是似乎都不如下面的文档全面。

  Quote:
(CREDIT: 这份文件的拷贝来自 Michael Jacobsen at http://www.jacobsen.sdn.dk/fdisk)

解密 FDISK  

背景

    此文主要讲述FDISK中你能使用的未公开的参数开关,这些开关可以使你以非交互的模式使用FDISK,例如,你可以在批处理文件中这样使用它。
    这些开关可以在Windows 95 OSR2(MS-DOS 71)包含的FDISK中的正常使用。
    有很多人曾经为本文作出贡献。如果你有任何评价、建议或问题,可以向我发送 e-mail
    我在FDISK中未发现删除已存在分区的方法,因此我自己写了一个小的工具程序(INSTHELP),用以清除分区表,请慎重使用。

此文中所用到的变量:
{size} 用 Mb 来表示的分区大小 - FDISK 会向上舍入
{disk} 硬盘号, 例如:第一硬盘就是 1

命令目录
/STATUS 显示当前的分区布局
/MBR 重建第一硬盘的主引导记录
/PRI 创建主分区
/PRIO 创建 FAT16 格式的主分区
/EXT 创建扩展分区
/LOG 创建逻辑驱动器
/LOGO 创建 FAT16 格式逻辑驱动器
/FPRMT 在交互模式中询问 FAT32/FAT16 格式
/Q 退出时跳过强制重启,此信息由 Mike Cowen 和 Paul Helsen 提供
/X 不使用 LBA 分区,此信息由 Francisco 提供
/ACTOK 不检查磁盘完整性,此信息由 Svend Christensen 提供
/CMBR 重建指定硬盘的主引导记录,此信息由 Francisco 提供
  
/STATUS – 显示当前的分区布局
    FDISK /STATUS
==如果你有一个未定义逻辑驱动器的扩展分区,将不会显示扩展分区的信息。

/MBR – 重建第一硬盘的主引导记录
在主引导记录被病毒感染时此功能很有用,使用 /MBR 可清除病毒。
    FDISK /MBR

/PRI – 创建并激活主分区
在号码为 {disk} 的磁盘上以 {size} 的大小创建并激活主分区。
    FDISK /PRI:{size} {disk}
==如果 {size} 大于硬盘的大小,则主分区会占用所有空间。

/PRIO – 创建并激活 FAT16 分区格式主分区
用法同 /PRI

/EXT – 创建扩展分区
在磁盘{disk}上创建大小为{size}的扩展分区(放置逻辑驱动器)。
    FDISK /EXT:{size} {disk}
==如果 {size} 大于剩余的可用空间,则扩展分区会占用所有可用空间,以避免使用此开关时不知道可用空间的准确大小。

/LOG – 创建逻辑驱动器
使用 /LOG 可以创建大小为 {size} 的逻辑驱动器。须与 /EXT 联合使用。
    FDISK /EXT:{size} {disk} /LOG:{size}
==/LOG 必须与 /EXT 联合使用,而且两个开关的 {size} 必须相同。此外,{size} 必须小于或等于可用空间。

/LOGO – 创建 FAT16 分区格式逻辑驱动器
用法同 /LOG

/FPRMT – 在交互模式中询问 FAT32/FAT16
使用 /FPRMT 你不会在启动屏幕中被询问是否支持大硬盘,但会在每次创建分区时询问 FAT16/FAT32 的分区格式。
    FDISK /FPRMT
==由 Jeff Richards 提供的附加信息: 注意分区格式询问对所有分区都是有效的,所以这个选项可用来强制 FDISK 创建小于 540MB 的FAT32 分区(默认情况下,对于只对大于 540 MB 的分区使用FAT32格式)。

/Q – 跳过退出 FDISK 时的强制重启电脑
使用 /Q 将不会在修改分区表后重启电脑,但是我不记得DOS 7X的FDISK会重启电脑,也许是因为我经常使用的是引导盘的FDISK吧。
    FDISK /Q
==我无法验证此开关,Mike Cowen 和 Paul Helsen 提供此信息

/X - 不使用LBA 分区
使用 /X 你不会得到 LBA 分区,详细情况参见 Micro Firmware Technical Support。
    FDISK /X
==我无法验证此开关,Francisco 提供此信息。

/ACTOK – 不检查磁盘完整性
使用 /ACTOK ,将不检查磁盘的完整性,加快 FDISK。
    FDISK /ACTOK
==注意: 它并不总是有效的,请告诉你使用 /ACTOK 的经验。我无法验证此开关,Svend Christensen 提供此信息。

/CMBR – 在指定硬盘上重建主引导记录
除了需要制定磁盘号之外,用法同 /MBR。
    FDISK /CMBR {disk}
==我无法验证此开关,Francisco 提供此信息。

/PRI, /PRIO and /LOG, /LOGO 的注解

据我所知,当使用 PRI 和 LOG 创建分区时,如果分区大于512MB时则创建 FAT32 分区,如果小于512MB则创建 FAT16 分区。 PRIO 和 LOGO 则不论分区是否大于512MB都将创建 FAT16 分区(效果类似于DOS5/6 中的 FDISK )。

用管道传送按键

你可以使用包含回车符{CR:0x0D}的键击文件向FDISK传送按键,大多数编辑器不能插入不含换行符{LF:0x0A}的回车符{CR:0x0D},所以你需要使用十六进制编辑器完成此任务。请看CREATE.TXT 和 ERASE.TXT。

例 1a – 创建扩展分区:
    FDISK < CREATE.TXT
例 1b – 不检查磁盘完整性地创建扩展分区:
    FDISK /ACTOK < CREATE.TXT
例 2 – 删除例 1a/1b 创建的扩展分区:
    FDISK < ERASE.TXT
所有管道传送信息均由 Svend Christensen 提供。

声明:本文中所涉及得到的 FDISK 和其他程序有很高风险,请自负使用责任。

Michael Jacobsen 1999年8月22日更新


作者: 英雄不色     时间: 2005-1-22 00:00
willsort,您好.请问在批处理中如何实现段注释呢?我的思路是这样的.doskey /*=goto %explain%doskey */=:%explain%问题是如何才能将变量%explain%有序增值.站长在批处理一文中提到了"COUNT是变量的计算工具,如原来C的值为1,执行COUNT C后C的值就为2。BE是个强大的批处理增强工具,在Norton Utilties 8.0中带有。KPUSH是键盘缓冲工具。"这个COUNT命令请问您有吗?还有一个问题.我想在批处理中也会常用到.那就是如何把键盘输入加载到变量.有没有像input这样的命令.比如:
@echo offecho 请输入您的名字:input %1在运行到input后就会提示用户输入.待用户输入后将用户输入的保存到变量中,以便调用.期待您的答复!先说声谢谢了.
作者: willsort     时间: 2005-3-18 00:00
Re 英雄不色:

  很抱歉,因为鲜有关注此帖的朋友,所以我也丧失了对他的关注的热情。不过见到你的问题,很是高兴。

    下面是在兄所发的新主题中所作回复,全文引用:

  Quote:
    的确如dosforever兄所言,DOSKEY宏命令只能用于命令行,而不能用于批处理。
    一般来说,批处理的块式/段式注释不会大量出现,因为批处理本身的体积不会很大,如果很大,用rem 或者::也是理想的。你所说的注释方式大概主要用于批处理的语句调试过程中,当同时调试许多个区段时(这种情况也很少见),才会有自动增加注释标签号的需求。

  另外,批处理的段注释有一种比较常用的方法:
     goto start
     注释内容,
      = 可以是多行文本,
      = 可以包含重定向符号和其他特殊字符
      = 只要不包含 :start 这一行
     :start
  另外,还有其他各种注释形式,比如:
        1、:: 注释内容(第一个冒号后也可以跟任何一个非字母数字的字符)
        2、rem 注释内容(不能出现重定向符号和管道符号)
        3、echo 注释内容(不能出现重定向符号和管道符号)〉nul
        4、if not exist nul 注释内容(不能出现重定向符号和管道符号)
        5、:注释内容(注释文本不能与已有标签重名)
        6、%注释内容%(可以用作行间注释,不能出现重定向符号和管道符号)
        7、goto 标签 注释内容(可以用作说明goto的条件和执行内容)
        8、:标签 注释内容(可以用作标签下方段的执行内容)

     诸如此类,不一而足。
------------------------------------------------
2005.07.18

  最近的发现表明,在Win9x和MSDOS平台下,可以在批处理中可以通过特殊的方法调用DOSKey的宏命令,但是它仍然对段注释的主题无法起到明显的助益作用。示例如下:
  echo macro_name | call
  

[ Last edited by willsort on 2005-7-18 at 14:18 ]
作者: willsort     时间: 2005-9-26 23:02    标题: if语句中防空字符的选择



  Quote:
  经常编写批处理的人,想必对 "if string1==string2 command arguments"这样的句式耳熟能详。因为 string1/2 可以引用变量,所以有可能出现空值,所以就需要防止 string1/2 为空时 if 语句出现的语法错误。

  而我们最常使用的方法就是防空字符,最初防空字符的选择有很多,比如:

  if #%1==# goto end
      if [%1]==[] goto end
      if %1!==! goto end

  但是,随着批处理的深入使用,以上的所有防空字符被淘汰了,关键的原因就是他们不能避免当 string1/2 中含有空格时所出现的语法错误,比如下面的语句是语法错误的:

  if [%1]==[my project] goto end

  此时,成对的双引号似乎成了唯一的选择:

  if "%1"=="my project" goto end

  然而,它仍然不是最佳的选择,如果 string1/2 中同时出现了引号和空格,那么我们所厌见的语法错误又会见面了:

  if "%1"=="my project:"code for oop"" goto oop

  接下来,我们该怎么办呢?别问我,我不知道答案!

以上内容转自
{16327}批处理疑难征解:防空字符的选择
http://www.cn-dos.net/forum/viewthread.php?tid=16327
作者: willsort     时间: 2005-9-26 23:05    标题: 引用环境变量中路径时的防御字符的选择



  Quote:
  另一个与此相关的问题是,路径变量后的防御字符。

  当我们使用环境变量引用一个路径时,一般是直接引用,比如:

  %temp%\_temp.bat

  但这样存在一个问题:如果%temp%是某个驱动器的根目录,比如C:\,那么以上的引用就变成了:
  
  C:\\_temp.bat

  路径中出现了双斜线,这在MS-DOS和Win9x中将会引起语法错误,所以有人采用了在变量后加一个句点的做法,即:

  %temp%.\_temp.bat

  这样的话,如果%temp%为根目录,引用结果就是:C:\.\_temp.bat,如果不是根目录(比如C:\temp),就是C:\temp.\_temp.bat。

  这利用了句点在不同用法中所呈现出的二义性:在前一用法中,它表示当前目录,而根目录下的当前目录自然仍然是根目录;而在后一用法中,它表示目录名中主名与扩展名的分隔符,因为句点后没有实际的扩展名,所以C:\temp.仍然表示的是C:\temp目录。

  但是,这带来了一个新问题:如果路径中出现了相对路径的特殊引用符,该怎么办?比如,如果%temp%是表示当前目录的 . 或者上一级目录的 ..,那么 %temp%.\_temp.bat 的引用就变成了:

  ..\_temp.bat 或者 ...\_temp.bat

  这显然又不是我们所需要的结果,那么我们该怎么办呢?

  答案在你们的手里 :-)

以上内容转自
{16327}批处理疑难征解:防空字符的选择
http://www.cn-dos.net/forum/viewthread.php?tid=16327
作者: willsort     时间: 2005-9-26 23:29    标题: 目录的存在判定

这似乎是个很简单的问题,在我见过的和我编过的绝大部分批处理中,目录判定都使用的是这个经典格式:

      if exist directory\nul command argument(s)

      可以说,这个技巧的创始者确实对DOS的研究十分深刻,至少他发现了 nul 所代表的设备管理机制与文件管理机制之间复杂而微妙的关系,nul设备与. 和 .. 等一样可以在大部分路径下直接引用而不会产生系统错误。

      但是,也正像 .. 不能在根目录下引用(这在NT CMD中被修改了)一样,nul也有它的局限性。

      当我们在 MSDOS7.10 的环境中加载了 DOSLFN 以实现长文件名支持时, DOSLFN 的某些 BUG 会将 \NUL 的引用转义,此时会将文件判定为目录。

  Quote:
C:\>echo.>test.txt

C:\>if exist test.txt\nul echo Exist dir "test.txt"
Exist dir "test.txt"

C:\>

如果说这仅仅是doslfn的问题,那么我们可以禁止DOSLFN,然后再将测试阵地转到光盘上。由于光盘具有独特的文件系统CDFS,\NUL的引用失效了,目录仍然无法被识别出来,不过总算不会把文件误判作目录了。

  Quote:
E:\>dir /ad /b
BOOT
DOS71

E:\>if exist boot\nul echo Exist dir "BOOT"

E:\>

我们再转到时新的CMD@WinXP中,如果目录名中包含空格,则无法用长文件名识别其存在。

  Quote:
C:\>md "my test"

C:\>if not exist "my test"\nul echo Not exist "my test"
Not exist "my test"

C:\>if not exist "my test\nul" echo Not exist "my test"
Not exist "my test"

C:\>

[ Last edited by willsort on 2005-10-26 at 11:16 ]
作者: 无奈何     时间: 2005-9-27 00:41
关于目录的存在判定

用 if exist directory\nul command argument(s)
的方法只要目录含有空格无论如何是匹配不正确的。
我实际应用中一般用下面的方法:
md "my test"
cd "my test" &&echo Exist directory "test"
这样会很准确,但也是有缺点的,那就是会进入其目录。
作者: willsort     时间: 2005-9-27 17:10
Re 无奈何:

      上文因为时间关系,并未完成就贴出了,所以遗漏了很多问题。

      对于含空格目录名,可以使用8.3短名判断,或者也可以使用以下的方法:

      if exist "my test\*" echo Exist directory "my test"

      当然,与你的方法一样,这只使用于NT系列的命令行,兼容性上很不够好。

      在DOS下,我正在考虑dir+find的方案,不太成熟,如下:

      dir directory /ad | find ".." i>nul
      if not errorlevel 1 echo Exist directory "directory"

      但是它也有缺点,比如无法正确判定类似 “e:\” 这样的目录。

      而关于你的md+cd的方案,进入目录倒不太要紧,可以cd..退出,但是它无法判断跨分区路径,需要改用 cd /d ,返回路径时却更麻烦,另外还需要禁止 C命令可能出现的错误消息。

      pushd "e:\test_path" 2>nul && echo Exist directory "e:\test_path" && popd

      但是,只能在NT下使用的方案,局限性还是太大了。

[ Last edited by willsort on 2005-9-27 at 17:55 ]
作者: willsort     时间: 2005-10-13 11:29    标题: 文件名中的扩展名的判定

文件名中的扩展名的判定



      此帖缘起于 英雄不色 兄在 {1515:8762} 的第7个问题。

  Quote:
7.如何在批处理中判断文件后缀?实现诸如以下的效果。

if "%1" == "*.dll" goto dll
if "%1""=="*.inf" goto inf

1、如果文件名所指的文件是单个文件,且存在这个文件,且其中不包含路径的指定,那么可以使用以下的方案;
for %%x in (*.dll) do if exist "%1" goto dll
for %%x in (*.inf) do if exist "%1" goto inf
2、如果文件名所指的文件是单个文件,但是它不存在,且其中不包含路径的指定,那么可以使用以下的方案;
if not exist %temp%\_getext_\nul md %temp%\_getext_
echo _getext_> %temp%\_getext_\%1
if exist %temp%\_getext_\*.dll goto dll
if exist %temp%\_getext_\*.inf goto inf
3、如果程序应用环境是NT系列的命令行,且文件名中只有一个分隔主文件名和扩展名的句点,可以使用以下的方案;
for /f "delims=. tokens=2" %%x in ("%1") do set _ext=%%x
if "%_ext%"=="dll" goto dll
if "%_ext%"=="inf" goto dll
4、使用Strings、aset等可以获取文件扩展名的第三方工具,不过它们对长文件名和中文文件名的支持可能不很完美。

[ Last edited by willsort on 2005-10-13 at 13:01 ]
作者: 不得不爱     时间: 2005-10-13 13:21
有SPFDISK的批处理参数吗?
作者: 无奈何     时间: 2005-12-2 22:41
to willsort

关于 PING 命令的一些问题,几经搜索也一无所获,只能截取 ping 命令执行前后 %time% 变量计算差值,实际测试看看规律。相信兄早已做过这样的测试,不具体贴数据了。总结如下:
ping -n x 127.0.0.1>nul 这种延时方式不论 x 值大与小误差基本相差不多,可以接受。
ping -n x -w y 0.0.0.1>nul 这种延时方式,延时的时间越长误差越大,甚至会大到离谱,不可以接受。
所以建议尽量不要用第二种方法。

另外关于 ping/n 1 127.1 可以看看这篇文章。
http://biz.chinabyte.com/255/2012255.shtml

补充一点,作者没有提到以零开头数字地址的识别如:ping 0123.1.1.1,其中以零开头的数字 0123 是会按 8 进制解析的,等效于 ping 83.1.1.1。
作者: willsort     时间: 2005-12-4 21:20
Re 无奈何:

      关于 ping 的延时问题,我最后得出的结论,也是采用3.1,而不采用3.2。

      方案3.2至少存在以下问题:

         1、ping 发送报文时似乎有一定的延时,而这个延时并非固定的事我曾提到的500毫秒,而会随网络环境和系统环境发生变化。昨天的测值大约是200~300ms,今天则高一些,是400~500ms。

         2、ping 的 -w 超时特性需要更多的前提条件,我尝试将我的本地连接禁用后,超时未起作用。

      至于,你所提到的延时越长,误差越大问题,那可能是因为你通过 x 调节延时大小,可以通过调节 y 值(单位:毫秒)达到与方案 3.1 相类似的延时精度。

参考链接:
批处理编程的异类——时钟(Clock)
http://www.cn-dos.net/forum/viewthread.php?tid=8905#pid54227
作者: 无奈何     时间: 2005-12-5 00:06
:to willsort
x 与 y 的值我都调节过,延时长时尽量增大 y 值,延时短时尽量减小 y 值,倒是可以获得小的误差,但是时间换算比较麻烦。
作者: willsort     时间: 2006-1-10 15:03
下文转自本站解答室:
http://www.cn-dos.net/forum/viewthread.php?tid=12892#pid95281

关于 HDKP 的几点技术探讨
============================================================================

作者:Will Sort
日期:2005-04-22
版本:1-20050505
关键词:HDKP, %comspec%, 命令解释器, drive check, 磁盘检查, 命令行循环
摘要:对 HDKP 磁盘检查和磁盘遍历的在算法和细节上的相关讨论
----------------------------------------------------------------------------

背景

全名:Hard Drive Killer Pro
版本:通行版本为 4.0(包括 bat 版和 bat 压缩成的 exe 版),最新版本 5.0beta
作者:Munga Bunga(澳大利亚)
日期:4.0 版最晚成于2001年5月
平台:据原作者声称,此程序可以工作于 DOS/Win3.x/9x/NT/2000 等系统中,适用于
  世界上 90% 的电脑,彼时还没有 XP 和 2003
用途:毁掉或者说“清洗”电脑上所有硬盘、软盘和闪盘的所有数据。当然,因其历史
  悠久,当前流行的大部分杀毒软件都可将它杀除。
----------------------------------------------------------------------------

序言

  我最早在《批处理简单教程》中看到这篇转贴的批处理程序,教程作者在精彩程序
示例中将此程序列为压轴之作,当时因为我对一切带有所谓 Killer/Hacking 等字眼的
程序都抱有相当的敌意,所以当时只是草草瞥了两眼,并没有细看。后来,我又在“联
合 DOS论坛”中见到“神仙贝贝”兄发出的帖子中[01]转贴了这个程序,不过基于类似
的原因,我仍然只是翻看了一下有无值得留意的回复,随后顺手关闭了它,以至于后来
Climbing 兄的邀请也没有看到。

  4 月中旬我开始翻看“联合 DOS论坛”中关于批处理的旧帖,数次在 Climbing 兄
被置顶的精华帖索引[02]中看到“仔细学学”的建议,终于又点开了“神仙贝贝”的老
帖,此时早已有 wangsea兄将注释的工作漂亮的完成,我的任务就只是简单的享受他人
的劳动果实了。

  但是,遗憾的是,这篇程序里出现了太多的问题和漏洞。有些问题,比如那个无效
的格式化开关 /autoSample 和 :Sampledrv,显然都并非原作者的笔误,而像有心人或
者有心的程序特意修改的;另外一些问题,比如 dedecated 和 elapse,就不知是澳大
利亚的方言还是作者的无心之失了;剩下的一下问题,则可以肯定主要的责任人就是作
者本人。

  当然,为了讨论的公允和普适,我找到了这个程序的原始链接[03]并试图下载,可
是下载失败,我又尝试了其他几个方法,但找到的版本都与“神仙贝贝”的并无不同。
最后,我决定仍然采用后者作为讨论的基础,所以我讨论的只是代码的得失,而非作者
的得失。
----------------------------------------------------------------------------

正文

  无可否认的是,程序中确实使用了一些高级技巧,这些技巧最主要的体现就是磁盘
检查程序(drivechk.bat),但需要注意的是, drivechk.bat 并非作者本人原创,它
的原作者是 Tom Lavedas[04],这也正是作者“Except for”的原因。但是,可能作者
并没有透彻理解这个程序,只是进行了一些简单的改动,也可能作者引用的是 Lavedas
早期的程序,所以这段代码存在着一些问题。

  下面是程序中动态生成 drivechk.bat 的一段

echo @echo off >drivechk.bat
echo @prompt %%%%comspec%%%% /f /c vol %%%%1: $b find "Vol" > nul >{t}.bat
%comspec% /e:2048 /c {t}.bat >>drivechk.bat
del {t}.bat
echo if errorlevel 1 goto enddc >>drivechk.bat

  此段的目的是将下面的代码写入 drivechk.bat 之中。其过程 wangsea 兄已经分
析过,我不再细述。

%comspec% /f /c vol %1: | find "Vol" > nul
if errorlevel 1 goto enddc

  第一,“echo @echo off >drivechk.bat”一句不是必须的。因为 drivechk.bat
是一个动态生成的被调程序,它在被 call 时,会继承主调程序的 echo off 状态。

  只有用 %comspec% /c 调用时,“echo off”才可能被需要,因为 %comspec% /c
会创建一层新的命令解释环境,在这个新环境中 echo 状态会被复位。也正因此,程序
才能通过 %comspec% /e:2048 /c {t}.bat 获得被修改的命令行提示符,它就是要写入
drivechk.bat 的语句。

  第二,第二行中的“%%%%comspec%%%%”完全可以改为“%comspec%”。“%%%%”的
作用是延迟 %comspec% 的替换,使得 %comspec% 直到执行 drivechk.bat 时才被替换
为命令解释器的路径;但是这样是没有必要的,因为被调的 drivechk.bat 和主调程序
必定是运行在相同的环境下,所以替换的迟或早并没有什么分别。

  而且,我建议直接将 %comspec% 改为 command 。因为在 NT 类的命令行环境中,
%comspec% 通常指向 cmd.exe ,它与 command.com 有很多不同,比如在非英文系统中
的代码页不同,导致 find "Vol" /c 失败。而在早期的 MS-DOS 环境中, %comspec%
也有可能指向 NDOS 等非标准的命令解释器,它们的开关参数以及执行输出都可能导致
%comspec% 一句失败。而直接使用 command 最大的问题,就是它不处于当前路径或者
%Path% 路径中,但那几乎是不可能的。

  第三,“find "Vol" > nul”应该是“find "Vol" $g nul”,否则“> nul”将不
能写入 drivechk.bat ,从而可能在调用 drivechk 时造成 find 的消息“泄露”;而
在程序调用时,作者试图通过“call drivechk.bat %%a >nul”来避免了这个隐患,但
是他可能不清楚这个“>nul”对于 call 批处理是无效的。

  只有通过 %comspec% /c 来调用批处理,其输出才能被重定向或被管道传递。但即
便如此,执行 vol %1:可能有一些输出仍然无法被屏蔽,比如访问了不存在或未准备的
盘所产生的错误消息,因为它所使用的是错误设备输出,而并非标准控制台,所以不通
过管道被传递。解决办法是在 %comspec% /c 的前后加上一对 ctty nul 和 ctty con
语句。但是, ctty 只在 MS-DOS 和 9x 命令行被支持,在 XP 等 NT 类命令行 ctty
被取消了。在此类环境中,只能通过 2> nul 重定向错误消息。

  第四,“if errorlevel 1 goto enddc”忽略了很多问题。在 MS-DOS 或者 9x 命
令行中用 vol 访问存在但未准备好的盘,会有 Vol 信息输出,但程序此时并不会跳转
到结束;另外,就是可能访问到只读性的光盘或闪盘,也会有 Vol 信息输出。

  所以,程序为了补救者两个缺陷,才在后面使用了 dir | find "bytes" 检测准备
的准备情况,用 dir | find "0 bytes" 再检查只读的光盘。但被锁定的闪盘仍然无法
正确判断,可能在程序完成时,闪盘的锁定功能尚不常见吧。

  检测磁盘可读的语句也可以替代以 dir %1:\nul|find "\" ,而检查磁盘可写可以
使用 copy /y _writed_.tag %1: 和 if exist %1:_writed_.tag 来判断[05],当然这
些语句仍然都需要在 %comspec% /f/c 的外壳中运行。但是,仍然有些我们想不到的问
题,比如我在虚拟PC的共享盘中只能创建和修改文件,但却不能删除它。其他诸如网络
盘、内存虚拟盘等等,我没有对它们做更为细致的讨论。在这些细节上,专业程序也许
做得更好也不一定。

  第五,程序中还有一个较简单的技巧,就是在 :Sampledrv 中磁盘的遍历。对于使
用 For 语句进行循环式的遍历,可能是批处理编程的惯例,然而这个惯例在某些情况
下可能并不是最优的。

  事实上,在很早的时期,就有人使用更为灵活的命令行循环来处理很多 For难以解
决的问题。比如在 do 后面有多条语句时,我们必须将这些语句放在批处理中被 call
调用,但是 for的特性决定了它运行时的多次重复调用,而命令行循环的优越性在于它
只需要 call 一次,剩下的只是程序内部的 shift和 goto 。这节省了相当多的性能,
因为 call 远比 shift和 goto 占用更多的性能资源。

  第六,我们讨论 drivechk.bat 动态生成的必要性。这里并不是说 drivechk.bat
的必要性,而是说动态生成的必要性。我们可以猜测,程序之所以要费尽周章地动态生
成这段代码,而不是把它静态地嵌入在主程序中,其主要目的是可以利用它进行磁盘检
查的循环遍历。然而,可能作者并没有注意或者意识到,使用静态代码也可以完成同样
的任务。这意味着整个程序在写入 drivechk.bat 上所绕的所有的弯路都是不必要的,
我们完全可以找到一条更直接简短的道路。

  将程序所需要多次利用的代码作为一个模块嵌入到自身之中,然后再反复调用这个
模块即可达成与调用动态代码相似的效果。模块的通俗化理解就是以一个标签起始,以
goto end结束的一整段代码,而调用这个模块之需要配合使用 call %0 : lablename
和 if "%1"==":" goto %2 即可。

主调程序的结构
CODE:  [Copy to clipboard]
...
:: Call 型调用自身
call %0 : labelname
...
:: Goto 型调用自身
%0 : lablename
...
:: Call 型调用其他Bat
call bat_name : labelname
...
:: Goto 型调用其他Bat
bat_name : lablename
...
被调程序(可能也是主调程序)的结构
CODE:  [Copy to clipboard]
@echo off
if "%1"==":" goto %2
...
:labelname
:: 模块的代码段
...
goto end
...
:end
----------------------------------------------------------------------------

结语

  我写作此文的目的,正如我在摘要中所提到的,是试图探讨关于磁盘检查和命令行
循环的一些技术特性和细节,而并非是为 HDKP 或者类似的非善意程序提供技术支持。
关于 HDKP 的编写目的,我个人仍然持反对态度。此程序大概正如作者所希望的,只有
对“a very special person that does not want to be named”才具有实用性。对于
我来说,它仅仅是一个技术和构思的不完善结合体。

  我知道早已经有专业的第三方程序可以实现磁盘检查的功能,比如 dready.com 。
但是,作为批处理编程的一种技术门类,讨论它仍然是有益的,它可以帮助我们更加深
刻地理解系统工作原理,并且熟悉我们所工作的环境。另外,作为批处理来说,不依赖
于第三方程序,仍然有着相当的优势和价值。
----------------------------------------------------------------------------

参考:

01. 希望有哪位朋友能给这个批处理文件写一份完整的注释
  http://www.cn-dos.net/dosbbs/dispbbs.asp?boardid=9&id=12892

02. DOS联盟论坛解答室精华帖索引
  http://bbs.cn-dos.net/dispbbs.asp?boardid=9&id=13226

03. Hard Drive Killer Pro (HDKP) - The Hackology Network
  http://www.hackology.com/programs/hdkp/ginfo.shtml

04. Safely Testing the Status of All Types of Drives - Tom Lavedas
  http://www.pressroom.com/~tglbatch/testdrv.html

05. Avoiding Drive Not Ready - alt.msdos.batch
  http://groups-beta.google.com/gr ... owse_thread/thread/
    df0213182e13de5b/fa2abe726f972567?q=%22:_end%22+label+name&rnum=2&hl=en
=============================================================================
作者: maya0su     时间: 2006-1-15 01:11
TO:willsort
有这样一个问题:
@echo off
xcopy  \\computername\filename
start  e:\dos\name.exe
此批处理在运行过程中没有任何错误!
但是用bat3com转化成.com后,最后一句 start  e:\dos\name.exe无法执行!
作者: willsort     时间: 2006-1-15 19:48
Re maya0su:

      抱歉!我已经有四年时间没有使用过 bat2com 程序了,对其中的细节我无法肯定。可能是 start 命令的命令行参数在被 bat2com 编译后无法正确传递。如果可能,我建议使用 Call 或直接调用 name.exe 。

      另外,需要说明的是,bat2com 类软件至今为止仍具有很大的应用限制,比如对重定向的处理很不理想等等,所以我一般不使用它们对批处理进行编译,如果有此方面的需求,我宁可选择使用 WinRAR 压缩成可执行包,并设置自动运行与删除参数,可惜如果在 DOS 下应用此方法,还有相当大的限制。

[1]寻找一款制作特殊自解压包的压缩软件
http://www.cn-dos.net/forum/viewthread.php?tid=15721

[2]WinRAR 制作的批处理程序自解压包
附件 1: CompEXE.rar (2006-1-15 19:48, 226 K, 下载附件所需积分 1点 ,下载次数: 116)

作者: redtek     时间: 2007-2-8 07:05
好帖子再次欣赏~:)
作者: xycoordinate     时间: 2007-3-14 06:26
无意中发现!

顶上去!
作者: Billunique     时间: 2007-3-30 06:34
虽然还来不及细看,不过像这样的好贴一定要顶,不能让它沉下去!
作者: logictianjin     时间: 2007-4-11 00:25


  Quote:
我们再转到时新的CMD@WinXP中,如果目录名中包含空格,则无法用长文件名识别其存在。


  Quote:
C:\>md "my test"

C:\>if not exist "my test"\nul echo Not exist "my test"
Not exist "my test"

C:\>if not exist "my test\nul" echo Not exist "my test"
Not exist "my test"

C:\>

if 命令不能用于直接测试目录,但空 (NUL) 设备确实存在于每个目录中。因此,可以通过测试零设备确定目录是否存在。以下范例可测试目录的存在:

if exist c:mydir\nul goto process


用帮助上的范例可以用长文件名检测到带空格的文件夹是否存在,可能我对前辈的题目理解不清楚,因为是新手,如果回复的有错误请见凉!

我的理解确实出现问题了,错误在于:
没有与带空格的文件夹同名的文件才会检测成功(如果文件带有扩展名,也会检测成功,除非这个文件没有扩展名)否则会失败!

[ Last edited by logictianjin on 2007-4-11 at 01:51 PM ]
作者: rokersong     时间: 2007-11-10 15:00
无意在此灌水,但真的想说,你们真的很棒
加油
作者: mxw0934     时间: 2008-8-28 09:20
DOS联盟真是不错!
作者: Lying     时间: 2010-6-26 10:17
迷糊
作者: zzz19760225     时间: 2017-12-3 00:19

作者: doscndu     时间: 2020-1-12 23:14    标题: 谢分享


作者: zyue     时间: 2021-7-20 18:09
赞赞赞赞赞赞