如何从命令提示符*不带换行符*发送 EOF?
How to send EOF from command prompt *without newline*?
当然,要从命令提示符发送 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。 (如果您按 Enter,那么您键入的任何 ^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
而 type
命令在 Unicode (UTF-16LE) 输出模式下不是 运行。在这种情况下,唯一的 ^Z
处理是在 ReadFile
调用本身中,对于控制台输入缓冲区,如果一行以 ^Z
.
让我们用附加的调试器检查一下,注意读取的字节数 (lpNumberOfBytesRead
) 是第 4 个参数(x64 中的寄存器 r9),它是 return 通过引用编辑的输出参数。
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(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'
)保留在输入缓冲区中,这在您希望多个控制字符有不同的行为时很有用。
当然,要从命令提示符发送 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。 (如果您按 Enter,那么您键入的任何 ^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
而 type
命令在 Unicode (UTF-16LE) 输出模式下不是 运行。在这种情况下,唯一的 ^Z
处理是在 ReadFile
调用本身中,对于控制台输入缓冲区,如果一行以 ^Z
.
让我们用附加的调试器检查一下,注意读取的字节数 (lpNumberOfBytesRead
) 是第 4 个参数(x64 中的寄存器 r9),它是 return 通过引用编辑的输出参数。
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(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'
)保留在输入缓冲区中,这在您希望多个控制字符有不同的行为时很有用。