2017-05-09 83 views
4

当然,从命令提示符发送EOF,输入然后按Ctrl-Z是否有效。如何从命令提示符发送EOF *不带换行符*?

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

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

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

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


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

+6

'copy con file.txt'也应该像你想要的那样工作。 – eryksun

回答

4

命令type con > file.txt没有在CMD外壳^Z任何特殊处理,因为目标文件不是contype命令没有在运行的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')仍保留在输入缓冲区中,如果您想为多个控制字符使用不同的行为,这很有用。

+0

谢谢@eryksun!对引擎盖下正在发生的事情进行非常全面和有趣的分析... – mksios

相关问题