当然,要从命令提示符发送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
而不执行发送EOF。 (如果你按 Enter ,那么你键入的任何^Z
都会被写为文字转义字符!)
如果在Windows上无法做到这一点,为什么?
1 https://askubuntu.com/questions/118548/how-do-i-end-standard-input-without-a-newline-character
答案 0 :(得分:4)
命令type con > file.txt
对cmd shell中的^Z
没有任何特殊处理,因为目标文件不是con
且type
命令不是' t以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
个字符。我们可以在调试器中看到这一点,但这次我们处于完全未记录的区域。在检查时,似乎该函数的第3个参数(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(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'
)保留在输入缓冲区中,如果您想要多个控制字符的不同行为,这将非常有用。