ITEEDU

至于本节最后一个问题--命令行参数则是一个十分重要的内容,所谓重要主要表现为这个技术很常用。那么究竟何为"命令行参数"呢?这是我们首先要讨论的问题。

我们知道DOS的所有功能都是以"命令"的形式提供给我们的,当我们需要DOS为我们做一些事情的时候我们要在DOS提示符后面打入一个命令,如COPY或TYPE等。而我们如果需要运行一个可执行程序时也要在提示符后打入程序名,比如FORMAT等。一般情况下我们把这样一种人机交互方式称为"命令行"方式。

所谓的"命令行参数"指得是在命令或程序名后面打入的一些具有某种特殊意义的字符。以DIR命令为例,如果我们仅仅打入"DIR"这命令,DOS会按顺序显示磁盘目录;如果我们打入"DIR/P",那么DOS就会在显示一个屏幕的目录名后暂停并等待按键。这个命令中出现的"/P"就是我们所说的命令行参数。再比如程序FORMAT.COM,我们使用这个程序格式化磁盘时必须要指出相应的驱动器,假如我们要格式化的软盘在"A:"中,这时我们需要打入"FORMAT A:"命令,其中的"A:"也是命令行参数。

需要指出的是有些参考书把"/"后面的字符称为"开关",把用空格隔开的字符称为"参数"。比如FORMAT程序的这样一个用法--"FORMAT A:/S",其中的"A:"是参数,"/S"是开关。这两种叫法是比较正规的,不过笔者并没有采用这样的叫法,原因是对于设计程序来说它们的处理方法没什么区别。

说了这么多东西大家好象觉得"命令行参数"是个很神秘的家伙,其实不然。命令行参数很容易处理,至少不是那么难以寻觅。我们现在就来在内存中找一找,看看它究竟出现在哪儿。请大家在DOS提示符后打入下面这个命令:

 C:\ASM\>debug tasm.exe aaa bbb[Enter]

 笔者使用了"TASM.EXE"这个程序作为"调试"对象,其实可以选择任意一个".EXE"的文件。现在请大家回想一下当一个".EXE"文件被DEBUG调入后,段寄存器DS和ES指向谁所在的段?

段寄存器DS和ES指向"PSP"所在的段。我们现在就来看一看PSP中还有什么有用的数据:

-d0[Enter]
1027:0000		CD 20 FF 9F 00 9A F0 FE-1D F0 4F 03 92 09 8A 03		. ........O.....
1027:0010		92 09 17 03 92 09 81 09-01 04 01 00 02 FF FF FF		................
1027:0020		FF FF FF FF FF FF FF FF-FF FF FF FF 18 10 4C 01		..............L.
1027:0030		97 0D 14 00 18 00 27 10-FF FF FF FF 00 00 00 00		......'.........
1027:0040		07 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00		................
1027:0050		CD 21 CB 00 00 00 00 00-00 00 00 00 00 41 41 41		.!...........AAA
1027:0060		20 20 20 20 20 20 20 20-00 00 00 00 00 42 42 42		.....BBB
1027:0070		20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00		........

"金矿"并不难发现,注意PSP中偏移5CH和偏移6CH处的数据,我们给"TASM.EXE"的两个参数"aaa"和"bbb"就在其中。我们由此得出了第一个结论:程序所需的两个参数出现在PSP偏移5CH和6CH处,而且是以大写形式出现的。

通过这个实验我们也能发觉三个问题:第一,PSP偏移5CH和6CH处存放的数据都是"0",并不是真正的命令行参数,为什么要说参数出现在偏移5CH和6CH处呢?如果这个"0"是参数的一部分的话,那么它又有什么含义?第二,如果有更多的参数会有什么情况?第三,如果有开关符"/"又会有什么情况?为搞清这三个问题,我们再做三次实验。先看看那个奇怪的"0"是怎么回事,注意命令中加有下划线的内容:

C:\ASM\>debug tasm.exe a:xxx c:yyy[Enter]
-d0[Enter]
1028:0000		CD 20 FF 9F 00 9A F0 FE-1D F0 4F 03 92 09 8A 03		. ........O.....
1028:0010		92 09 17 03 92 09 81 09-01 04 01 00 02 FF FF FF		................
1028:0020		FF FF FF FF FF FF FF FF-FF FF FF FF 18 10 4C 01		..............L.
1028:0030		97 0D 14 00 18 00 27 10-FF FF FF FF 00 00 00 00		......'.........
1028:0040		07 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00		................
1028:0050		CD 21 CB 00 00 00 00 00-00 00 00 00 01 58 58 58		.!...........xxx	
1028:0060		20 20 20 20 20 20 20 20-00 00 00 00 03 59 59 59		.....yyy
1028:0070		20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00		........

问题清楚了,偏移5CH和6CH处存放的数据原来是驱动器号,我们做第一次实验时没有指定驱动器,所以DOS给我们填入了"默认"驱动器号。这个问题就有些奇怪了,难道DOS会把我们给出的参数都当做文件名来处理吗?说到此,笔者认为已经到了真正搞清这两个数据结构的时候了。

在PSP偏移5CH和6CH处有两个DOS提供给我们的十分重要的数据结构,这两个数据结构我们称之为"格式化未打开的FCB"。实际上每一个FCB的长度只有16个字节,其结构与一般的FCB相同。注意这两个FCB都是"未打开的",如果我们将第一个FCB打开了,那么第二个FCB就会被覆盖。

我们给出的命令行参数均被DOS作为文件名放在这两个FCB中,由于只有两个FCB,所以DOS只能处理两个参数,而且如果参数中出现了"/"之类的字符,那么DOS就会认为给出的参数是不合法的文件名而不予处理。请做下面的实验:

C:\ASM\>debug tasm.exe aaa /b ccc[Enter]
-d0[Enter]
1027:0000		CD 20 FF 9F 00 9A F0 FE-1D F0 4F 03 92 09 8A 03		. ........O.....	
1027:0010		92 09 17 03 92 09 81 09-01 04 01 00 02 FF FF FF		................	
1027:0020		FF FF FF FF FF FF FF FF-FF FF FF FF 18 10 4C 01		..............L.	
1027:0030		97 0D 14 00 18 00 27 10-FF FF FF FF 00 00 00 00		......'.........	
1027:0040		07 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00		................	
1027:0050		CD 21 CB 00 00 00 00 00-00 00 00 00 00 41 41 41		.!...........AAA	
1027:0060		20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20		.....?	
1027:0070		20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00		........

很明显,"/b"和"ccc"均未出现。由此我们可以看出,使用这两个FCB处理命令行参数是有很多限制的。事实上很多程序根本不使用这样的方法处理命令行参数,而是采用了另一种方法。我们将在本章的后面详细讨论,这里我们给出一个示例程序,以说明这两个FCB的应用方法:

LIST.ASM
        DATA  SEGMENT
              ASSUME    DS:DATA
        LINE  DB        ?
         MSG  DB        'Press any key to conti
        DATA  ENDS
			
        CODE  SEGMENT
              ASSUME    CS:CODE
        MAIN  PROC      FAR
              MOV       AX,DATA	;初始化ES寄存器指向数据段
              MOV       ES,AX
			
              MOV       DX,5CH	;DX指向第一个未打开的FCB
              MOV       AH,0FH	;打开这个FCB
              INT       21H
              OR        AL,AL	;文件正确打开了吗?
              JNZ       ERR_EXIT	;若没有打开文件,转ERR_EXIT
			
              MOV       BX,DX	;BX指向已经打开的FCB
              MOV       WORD PTR [BX+0EH],1	;设置记录长度为1字节
			
              MOV       SI,80H	;SI寄存器指向缺省的DTA
  READ_NEXT:
              MOV       AH,14H	;从文件中读取记录
              MOV       DX,5CH	;DX指向打开的FCB
              INT       21H
              OR        AL,AL	;记录正确地读入了吗?
              JNZ       ERR_EXIT	;若读取操作不正确,转ERR_EXIT
			
              MOV       AL,[SI]	;从DTA中取得一个字节
              CMP       AL,0AH	;取得的是换行符吗?
              JZ        READ_NEXT	;若是换行符,转READ_NEXT
			
              MOV       AH,0EH	;利用10H中断的0EH功能
              INT       10H	;输出AL寄存器中的字符
              CMP       AL,0DH	;输出的字符是回车符吗?
              JNZ       READ_NEXT	;若不是回车符则继续处理下一个字符
              MOV       AL,0AH	;若是回车符则同时输出一个换行符
              INT       10H
              INC       ES:[LINE]	;并且将行数计数器加1
              CMP       ES:[LINE],23	;判断是否已经输出了23行字符
              JNZ       READ_NEXT	;若输出不足23行,则继续处理下一个字符
			
              MOV       DX,OFFSET MSG	;DX寄存器指向MSG
              PUSH      DS	;暂存DS寄存器
              PUSH      ES	;通过堆栈将ES寄存器送入DS
              POP       DS
              MOV       AH,9	;输出字符串MSG
              INT       21H
              MOV       AH,0	;等待键盘输入
              INT       16H
              MOV       AX,0E0DH	;将光标移至下一行首部
              INT       10H
              MOV       AL,0AH
              INT       10H
              MOV       [LINE],0	;行计数器清0
              POP       DS	;恢复DS寄存器
              JMP       READ_NEXT	;处理下一个字符
   ERR_EXIT:
              MOV       AH,4CH	;结束进程
              INT       21H
        MAIN  ENDP
        CODE  ENDS
              END       MAIN

这个程序可以取代DOS提供的TYPE命令,它用于显示文本文件的内容,用法与TYPE命令一致。不过这个程序每显示一屏信息就会暂停一次,按一键后继续显示下一屏。当然,被显示的文件每一行的字符数要小于80个。

本章结束语

至此,有关FCB文件操作功能我们已经基本上讨论完了,当然还有一些很不常用的功能我们没有涉及,这些内容可以在其它的参考书上找到。通过对FCB功能的研究,我们可以发现FCB功能所具有的一些特点,归纳如下:

(1)FCB结构源于CP/M操作系统,所以在CP/M系统下编制的程序很容易移植到DOS系统下,这一点在DOS发展的初期具有重要的作用;
(2)FCB的结构对于我们编程来讲是可见的,一个FCB打开后,所有有关于这个文件的信息都保存于FCB中,可以供应用程序使用;
(3)应用扩展FCB可以存取具有特殊属性的文件,而且可以处理象卷标这样的目录;这是其它文件功能所不具备的能力;
(4)FCB功能将文件划分为记录,程序员可以根据需要任意设置记录长度;
(5)一般情况下使用FCB功能打开文件的数目没有限制,除非是在网络环境下装入了文件共享程序(SHARE命令)。不过要打开众多的FCB,需要在内存中开辟相应数量的文件控制块,这些都增加了内存的开销;
(6)FCB功能不能处理树型目录,也就是不能处理"路径名",因此FCB功能的应用范围受到了限制;
(7)FCB功能返回的错误信息很少,不利于程序处理错误。DOS的版本3对此做了改进,提供了一个新的功能调用59H以获得更多的错误信息,但应用起来仍不方便;