命令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