如何从命令提示符发送EOF *没有换行*?

当然,要从命令提示符发送EOF, 按Enter键后按Ctrl-Z就行了。

C:\> type con > file.txt line1 line2 ^Z 

这个工作, file.txt包含line1\r\nline2\r\n 。 但是,如果没有最后一个换行符,你怎么能这样做,以便file.txt包含line1\r\nline2

在Linux中,解决方法是按Ctrl-D两次1 。 但在Windows上的等效物是什么? 命令提示符将愉快地在一行的末尾打印^Z s而不发送EOF。 (如果你按回车键 ,那么你input的任何^Z都会被作为文字转义字符写入文件!)

如果在Windows上没有办法做到这一点,那为什么?


1 https://askubuntu.com/questions/118548/how-do-i-end-standard-input-without-a-newline-character

命令type con > file.txt在cmd shell中没有针对^Z任何特殊处理,因为目标文件不是con并且未以Unicode(UTF-16LE)输出模式运行type命令。 在这种情况下,唯一的^Z处理是在ReadFile调用本身中,对于控制台输入缓冲区来说,如果以^Z开头的行有一个未公开的行为来返回0字节。

让我们用一个附加的调试器来检查它,注意读取的字节数( lpNumberOfBytesRead )是第四个参数(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字符。 我们可以在调试器中看到这一点,但是这次我们完全没有记录的领域。 在检查时,看起来这个函数的第三个参数(寄存器r8在x64中)是作为输入参数读取的字节数。

让我们再次输入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(0x17)字符串"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 

这次扫描的长度只是^Z之前的5个字符:

 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立即终止的,甚至没有按下回车键。 ^D控制字符(即'\x04' )保留在输入缓冲区中,这对于多个控制字符需要不同行为的情况很有用。