命令type con > file.txt
没有在CMD外壳^Z
任何特殊处理,因为目标文件不是con
和type
命令没有在运行的Unicode (UTF-16LE)输出模式。在这种情况下,唯一的^Z
处理在ReadFile
调用本身中,如果一行以^Z
开头,那么对于控制台输入缓冲区而言,它具有未记录的行为以返回0字节读取。
让我们通过附带的调试器来检查它,注意读取的字节数(lpNumberOfBytesRead
)是第4个参数(x64中的寄存器r9),它通过引用作为输出参数返回。
C:\Temp>type con > file.txt
Breakpoint 1 hit
KERNELBASE!ReadFile:
00007ffc`fb573cc0 48895c2410 mov qword ptr [rsp+10h],rbx
ss:00000068`c5d1dfa8=000001e3000001e7
0:000> r r9
r9=00000068c5d1dfd0
0:000> pt
line1
KERNELBASE!ReadFile+0xa9:
00007ffc`fb573d69 c3 ret
0:000> dd 68c5d1dfd0 l1
00000068`c5d1dfd0 00000007
正如你在上面看到的,如预期的那样,读"line1\r\n"
是7个字符。接下来让我们进入"\x1aline2\r\n"
,看报多少字节ReadFile
写着:
0:000> g
Breakpoint 1 hit
KERNELBASE!ReadFile:
00007ffc`fb573cc0 48895c2410 mov qword ptr [rsp+10h],rbx
ss:00000068`c5d1dfa8=0000000000000000
0:000> r r9
r9=00000068c5d1dfd0
0:000> pt
^Zline2
KERNELBASE!ReadFile+0xa9:
00007ffc`fb573d69 c3 ret
0:000> dd 68c5d1dfd0 l1
00000068`c5d1dfd0 00000000
正如你看到的上面,这时候它会读取0字节,即EOF。在^Z
之后输入的所有内容都被忽略。
但是,您需要的是在输入缓冲区中出现^Z
的任何地方,而不需要这种行为。 type
会为你做这件事,但前提是它以Unicode模式执行,即cmd /u /c type con > file.txt
。在这种情况下,cmd确实有特殊的处理来扫描输入^Z
。但我敢打赌,你不需要一个UTF-16LE文件,特别是因为cmd不写一个BOM来允许编辑检测UTF编码。
你很幸运,因为它发生copy con file.txt
确实是你想要的。它在内部调用cmd!ZScanA
扫描每行以获得^Z
字符。我们可以在调试器中看到这个动作,但是这次我们完全没有文档记录。在检查时,看起来该函数的第三个参数(x64中的寄存器r8)是作为输入参数读取的字节数。
让我们进入7字符串"line1\r\n"
重新开始:
C:\Temp>copy con file.txt
line1
Breakpoint 0 hit
cmd!ZScanA:
00007ff7`cf4c26d0 48895c2408 mov qword ptr [rsp+8],rbx
ss:00000068`c5d1e9d0=0000000000000000
0:000> r r8; dd @r8 l1
r8=00000068c5d1ea64
00000068`c5d1ea64 00000007
输出时,扫描长度保持7个字符:
0:000> pt
cmd!ZScanA+0x4f:
00007ff7`cf4c271f c3 ret
0:000> dd 68c5d1ea64 l1
00000068`c5d1ea64 00000007
0:000> g
下一页进入23(0×17)字符串"line2\x1a Ignore this...\r\n"
:
line2^Z Ignore this...
Breakpoint 0 hit
cmd!ZScanA:
00007ff7`cf4c26d0 48895c2408 mov qword ptr [rsp+8],rbx
ss:00000068`c5d1e9d0=0000000000000000
0:000> r r8; dd @r8 l1
r8=00000068c5d1ea64
00000068`c5d1ea64 00000017
这次扫描的len GTH只有5个字符先于^Z
:
0:000> pt
cmd!ZScanA+0x4f:
00007ff7`cf4c271f c3 ret
0:000> dd 68c5d1ea64 l1
00000068`c5d1ea64 00000005
我们预计file.txt的是12个字节,它是:
C:\Temp>for %a in (file.txt) do @echo %~za
12
更一般地,如果Windows控制台程序想要实现接近Unix终端的行为的Ctrl + D处理,它可以使用宽字符控制台功能ReadConsoleW
,通过引用传递CONSOLE_READCONSOLE_CONTROL
结构作为pInputControl
。该结构的dwCtrlWakeupMask
字段是一个位掩码,用于设置哪些控制字符将立即终止读取。例如,位4启用Ctrl + D。我写了一个演示这种情况下,简单的测试程序:
C:\Temp>.\test
Enter some text: line1
You entered: line1\x04
你不能看到这个在上面的例子,但这个读立即按Ctrl + d终止,甚至没有按下Enter键。 ^D
控制字符(即'\x04'
)仍保留在输入缓冲区中,如果您想为多个控制字符使用不同的行为,这很有用。
'copy con file.txt'也应该像你想要的那样工作。 – eryksun