2017-05-09 4 views
4

Bien sûr, pour envoyer EOF à partir de l'invite de commande, Entrez suivi de Ctrl-Z fait l'affaire.Comment envoyer EOF depuis l'invite de commande * sans retour à la ligne *?

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

Cela fonctionne, et file.txt contient line1\r\nline2\r\n. Mais comment pouvez-vous faire la même chose sans la dernière ligne, de sorte que file.txt contient line1\r\nline2?

Dans Linux, la solution est de frapper Ctrl-D deux fois . Mais quel est l'équivalent sur Windows? L'invite de commande imprime heureusement ^Z s à la fin d'une ligne sans envoyer d'EOF. (Et si vous appuyez sur Entrée , toute ^Z s que vous avez saisi écrits dans le fichier comme caractères d'échappement littéral!)

S'il n'y a aucun moyen de le faire sous Windows, alors pourquoi?


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

+6

'copy con file.txt' devrait également fonctionner comme vous le souhaitez. – eryksun

Répondre

4

La commande type con > file.txt n'a pas de traitement spécial pour ^Z dans le shell cmd, car le fichier cible n'est pas con et la commande type n'a pas été exécutée en Unicode (UTF-16LE) mode de sortie. Dans ce cas, la seule manipulation ^Z est dans l'appel ReadFile lui-même, qui pour un tampon d'entrée de console a un comportement non documenté pour renvoyer 0 octets lus si une ligne commence par ^Z.

Examinons ceci avec un débogueur attaché, notant que le nombre d'octets lus (lpNumberOfBytesRead) est le 4ème argument (registre r9 en x64), qui est renvoyé par référence comme paramètre de sortie.

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 

Comme vous le voyez ci-dessus, la lecture "line1\r\n" est de 7 caractères, comme prévu. Ensuite, nous allons entrer "\x1aline2\r\n" et de voir combien d'octets ReadFile lectures seraient:

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 

Comme vous le voyez ci-dessus, cette fois-ci lit 0 octets, à savoir EOF. Tout tapé après ^Z a été simplement ignoré. Cependant, ce que vous voulez à la place est d'obtenir ce comportement en général, où ^Z apparaît dans le tampon d'entrée. type le fera pour vous, mais seulement si elle est exécutée en mode Unicode, c'est-à-dire cmd /u /c type con > file.txt. Dans ce cas, cmd a une manipulation spéciale pour analyser l'entrée pour ^Z. Mais je parie que vous ne voulez pas un fichier UTF-16LE, d'autant plus que cmd n'écrit pas de nomenclature pour permettre aux éditeurs de détecter le codage UTF.

Vous avez de la chance, car il arrive que copy con file.txt fait exactement ce que vous voulez. En interne, il appelle cmd!ZScanA pour numériser chaque ligne pour un caractère ^Z. Nous pouvons voir cela dans l'action dans le débogueur, mais cette fois nous sommes dans un territoire complètement sans papiers. Lors de l'inspection, il apparaît que le troisième paramètre de cette fonction (registre r8 en x64) est le nombre d'octets lus en tant qu'argument in-out.

Commençons à nouveau en entrant la chaîne 7 caractères "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 

En sortie, la longueur numérisée reste 7 caractères:

0:000> pt 
cmd!ZScanA+0x4f: 
00007ff7`cf4c271f c3    ret 
0:000> dd 68c5d1ea64 l1 
00000068`c5d1ea64 00000007 
0:000> g 

Entrez ensuite la chaîne de caractères 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 

Cette fois, la numérisation len GTH est seulement les 5 caractères qui précèdent le ^Z:

0:000> pt 
cmd!ZScanA+0x4f: 
00007ff7`cf4c271f c3    ret 
0:000> dd 68c5d1ea64 l1 
00000068`c5d1ea64 00000005 

Nous file.txt attendre à être 12 octets, il est:

C:\Temp>for %a in (file.txt) do @echo %~za 
12 

Plus généralement, si un programme de console Windows veut mettre en œuvre la gestion Ctrl + D qui se rapproche du comportement d'un terminal Unix, il peut utiliser la fonction de console de caractères larges ReadConsoleW, en passant une structure CONSOLE_READCONSOLE_CONTROL par référence pInputControl. Le champ dwCtrlWakeupMask de cette structure est un masque de bits qui définit les caractères de contrôle qui terminent immédiatement la lecture. Par exemple, le bit 4 active Ctrl + D. J'ai écrit un programme de test simple qui démontre ce cas:

C:\Temp>.\test 
Enter some text: line1 
You entered: line1\x04 

Vous ne pouvez pas le voir dans l'exemple ci-dessus, mais cette lecture a été immédiatement interrompue en appuyant sur Ctrl + D, sans même en appuyant sur Entrée. Le caractère de contrôle ^D (c'est-à-dire '\x04') reste dans le tampon d'entrée, ce qui est utile dans le cas où vous souhaitez un comportement différent pour plusieurs caractères de contrôle.

+0

Merci @eryksun! Une analyse très thorough_ et intéressante de ce qui se passe sous le capot ... – mksios