2017-05-09 113 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

相關問題