2017-02-21 6 views
6

Wenn ich versuche, mehrere Dateien in mehrere Ströme umgeleitet werden, wie im folgenden Beispiel, funktioniert alles wie erwartet:Warum kann ich nicht auf zwei Eingabeströme `0 <` und `3 <` in dieser Reihenfolge umleiten?

3< stream3.txt 4< stream4.txt echo/ 

So etwas funktioniert auch mit mehreren Strömen und Dateien und andere Befehle als echo/ oder sogar eine funktionelle Codeblock. Selbst die reservierten Ausgabeströme 1 und 2 scheinen ordnungsgemäß zu funktionieren.

aber sobald ich mit den folgenden Strömen in der angegebenen Reihenfolge tun:

0< stream0.txt 3< stream3.txt echo/ 

Die Hosting-Eingabeaufforderung Instanz sofort geschlossen wird.

Wenn ich die Reihenfolge der Ströme zu ändern, es funktioniert gut wieder:

3< stream3.txt 0< stream0.txt echo/ 

Warum wird die cmd Instanz unerwartet beendet, wenn ich die Eingabe-Umleitung versuchen 0 und 3 in dieser Reihenfolge zu streamen?

Ich verwende Windows 7 (x64).


Wenn ich öffnen Sie eine neue Eingabeaufforderung Instanz von cmd bevor ich versuche, die oben genannten Fehler Eingangs Umleitung ausgeführt:

cmd 
0< stream0.txt 3< stream3.txt echo/ 

ich die erscheinende Fehlermeldung lesen können - vorausgesetzt, die Textdateien enthalten ihre Basisname von einem Zeilenumbruch gefolgt:

'stream3' is not recognised as an internal or external command, operable program or batch file. 

Wenn ich die folgenden Code, kann ich sehen, dass beide Dateien tatsächlich umgeleitet werden:

cmd /V 
0< stream0.txt 3< stream3.txt (set /P #="" & echo/!#!) 

cmd /V 
0< stream0.txt 3< stream3.txt (<&3 set /P #="" & echo/!#!) 

Jeweilige Ausgang:

stream0 

Und:

stream3 

Was zum Teufel geht hier vor ?

Antwort

4

note: Dies ist eine Vereinfachung das, was in cmd geschieht, wenn die Befehl ausgeführt wird umgeleitet.

Beginnen wir mit dem angegebenen Befehl starten

0< file1 3< file2 echo/ 

Der Befehl analysiert wird und eine Darstellung der benötigten Umleitungen im Speicher, eine Art von Tabelle/Liste erstellt, die die Informationen über die Umleitung halten: die hand umgeleitet wird, den alten gespeicherte Griff, wo der Griff zeigen soll, wenn, umgeleitet ...

Redirection requests 
    ------------------------------- 
    redirect saved redirectTo 
    +--------+--------+------------ 
R1 | 0     file1 
    | 
R2 | 3     file2 

an dieser Stelle (nach dem Befehl Parsen) hat keinen Strom geändert.

Es gibt auch eine Systemtabelle, die behandelt, wo jeder der Dateideskriptoren (in unserem Fall die cmd Streams) wirklich zeigt.

File descriptors 
    ------------------ 
    points to 
    +----------------- 
0 | stdin 
1 | stdout 
2 | stderr 
3 | 
4 | 

note Dies ist nicht ganz richtig, die zugrundeliegende Struktur ein wenig komplexer ist, aber auf diese Weise es leichter zu sehen ist, wie es

arbeitet Wenn der Befehl ausgeführt werden soll wird die interne SetRedir Funktion aufgerufen. Es wird über die vorherige Umleitungsanforderungstabelle iteriert, wobei vorhandene Handles gespeichert und erforderliche neue erstellt werden. Der Anfangszustand ist

Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0     file1     0 | stdin 
    |           1 | stdout 
R2 | 3     file2     2 | stderr 
               3 | 
               4 | 

erstes Element aus Umleitungsanforderungstabelle (R1) abgerufen wird, um die Anforderungen Strom 0 bis file1 zu umleiten. Es ist notwendig, das aktuelle Handle zu speichern, um es später wiederherstellen zu können. Für diese Operation wird die _dup()-Funktion verwendet. Es wird ein Alias ​​für den übergebenen Dateideskriptor (Stream 0 in unserem Code) erstellt, wobei der kleinste verfügbare Dateideskriptor verwendet wird (Datenstrom 3 in der vorherigen Tabelle). Nach der Operation und alten Griff nahe speichert die Situation

R1[saved] = _dup(R1[redirect]); 
    _close(R1[redirect]); 

    Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0  3  file1     0 |   ---\ 
    |           1 | stdout  | 
R2 | 3     file2     2 | stderr  | 
               3 | stdin <<--/ 
               4 | 

Einmal gespeichert ist, wird die Umleitung durch das Öffnen der angeforderte Datei und Griffes in der Datei-Deskriptoren Tabelle Zuordnung offene Datei abgeschlossen.In diesem Fall übernimmt die _dup2() Funktion den Betrieb

_dup2(CreateFile(R1[redirectTo]), R1[redirect]); 

    Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0  3  file1     0 | file1 <<--- 
    |           1 | stdout 
R2 | 3     file2     2 | stderr 
               3 | stdin 
               4 | 

Die erste Umleitung erfolgt ist. Es ist Zeit, die gleiche Operation mit die zweite zu tun. Speichern Sie zuerst das alte Handle mit der Funktion _dup(). Dies wird den angeforderte Datei-Deskriptor (3) mit dem niedrigsten verfügbaren Deskriptors assoziieren (4)

R2[saved] = _dup(R2[redirect]); 
    _close(R2[redirect]); 

    Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0  3  file1     0 | file1 
    |           1 | stdout 
R2 | 3  4  file2     2 | stderr 
               3 |   ---\ 
               4 | stdin <<--/ 

Die Umlenkung durch Öffnen der Eingabedatei abgeschlossen ist, und es mit dem Dateideskriptor Assoziieren

_dup2(CreateFile(R2[redirectTo]), R2[redirect]); 

    Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0  3  file1     0 | file1 
    |           1 | stdout 
R2 | 3  4  file2     2 | stderr 
               3 | file2 <<--- 
               4 | stdin 

Die Umleitung wurde abgeschlossen und der Befehl wird ausgeführt, wobei der Stream 0 an file1 umgeleitet und der Stream 3 an file2 umgeleitet wurde.

Sobald dies erledigt ist, ist es Zeit, den Prozess rückgängig zu machen. ResetRedir() Funktion verarbeitet die Operation. Es verwendet erneut die Funktion _dup2(), um das gespeicherte Handle in den ursprünglichen Dateideskriptor zu übertragen. Hier stellt sich das Problem, wie die gespeicherte Descriptor

_dup2(R1[saved], R1[redirect]); 
R1[saved] = null; 

    Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0     file1     0 | file2 <<--\ 
    |           1 | stdout  | 
R2 | 3  4  file2     2 | stderr  | 
               3 |   ---/ 
               4 | stdin 

nun geändert wurde, wird der gleiche Vorgang mit dem zweiten Umleitung erfolgt

_dup2(R2[saved], R2[redirect]); 
R2[saved] = null; 

    Redirection requests       File descriptors 
    -------------------------------    ------------------ 
    redirect saved redirectTo    points to 
    +--------+--------+------------    +----------------- 
R1 | 0     file1     0 | file2 
    |           1 | stdout 
R2 | 3     file2     2 | stderr 
               3 | stdin <<--\ 
               4 |   ---/ 

Sobald die Umleitung der &0 Griffpunkte auf file2 und der stdin entfernt wurde Strom wird in &3 gespeichert. Dies kann als

@echo off 
    setlocal enableextensions disabledelayedexpansion 

    >file1 echo This is file 1 
    >file2 echo This is file 2 

    echo Test 1 - trying to read from stdin after redirection 
    cmd /v /c"(0< file1 3< file2 echo - test1) &  set /p .=prompt & echo !.!" 

    echo(
    echo(

    echo Test 2 - trying to read from stream 3 after redirection 
    cmd /v /c"(0< file1 3< file2 echo - test 2) & <&3 set /p .=prompt & echo !.!" 

getestet werden, dass

W:\>testRedirection.cmd 
Test 1 - trying to read from stdin after redirection 
- test1 
prompt This is file 2 


Test 2 - trying to read from stream 3 after redirection 
- test 2 
prompt This is typed text 
This is typed text 

W:\> 

generiert Es ist ersichtlich, dass in dem ersten Test die set /p von file2 gelesen hat, und in dem zweiten Test, versucht, von &3 zu lesen Der stdin Stream kann erreicht werden.

+0

Wow, ausgezeichnete Antwort, +1 !! Dies führte mich zu einem anderen Szenario, das fehlschlägt: '3 stream1.txt 3> stream3.txt echo /' ('cmd' hängt, bis ich' exit' eingebe), oder '2> stream2.txt 3> stream3.txt echo/'(umgeleiteter Handle' 2' scheint nicht richtig geschlossen zu sein); es sieht so aus, als wäre der interne Prozess für die Ein- und Ausgangsumleitung der gleiche ... – aschipfl

+0

Also ich denke, ich könnte in der Regel sagen: * »Nach Umleitung eines vordefinierten Handle' 0'/'1' /' 2', Verwenden Sie nicht das nächste freie Handle (weder für die Eingabe noch für die Ausgabeumleitung) für die gleiche Befehlszeile/den gleichen Block. «* – aschipfl

+2

@Aschipfl, Eingabe- und Ausgabeumleitung werden innerhalb der gleichen Funktion behandelt, innerhalb derselben Schleife Griff Ersatz. Der Hauptunterschied besteht nur darin, wie das neue Handle abgerufen wird. In Ihrem '1> stream1.txt 3> stream3.txt echo/'case,' cmd' wird nicht gehängt, es wird nur 'stdout' an' stream3.txt' gesendet. Die allgemeine Regel besteht darin, Umleitungen (wenn möglich) von höheren zu niedrigeren Strömen zu definieren. –

3

Sieht aus wie ein Fehler bei der Wiederherstellung der Streams, nachdem der Befehl beendet wurde. Betrachten Sie die folgende Batchdatei:

0< stream0.txt 3< stream3.txt findstr . 

findstr . 

<CON pause 

Der erste findstr ausgeben wird der Gehalt an stream0.txt wie erwartet.

Die zweite findstr wird unerwartet den Inhalt von stream3.txt ausgeben, um anzuzeigen, dass Stream 0 unerwartet umgeleitet wurde.

Wenn die Batchdatei beendet ist, wird die Befehlsshell, die sie ausführt, ebenfalls beendet, weil sie ein Ende der Datei auf dem aktuellen Standarddatenstrom sieht.

0< stream0.txt 4< stream3.txt findstr . 

findstr . 

:done 
<CON pause 

Run als cmd /c test7 oder als cmd /c test7 3< other.txt es:


Retrying einer meiner Experimente im Lichte der MC ND's answer, kann ich jetzt das gleiche Problem mit einem Strom anders als Strom 3. Betrachten test7.cmd reproduzieren zeigt nicht das unerwartete Verhalten, aber läuft als es tut. (Das war ein Facepalm-Moment; ich hätte dies gestern entdecken können, wenn ich genauer darüber nachgedacht hätte. Offensichtlich muss die anfängliche Umleitung im Kontext der gleichen Befehlsshell sein, die das Stapelscript ausführt.)

So Die Ursache ist nicht "Stream 3 und Stream 0 umleiten", sondern "den ersten freien Stream sowie den Stream 0 umleiten". :-)

+0

Schönes Ergebnis, +1! Die Frage ist für mich, warum passiert das nur mit Stream 3? Es scheint mir, dass Stream 3 nicht vollständig undefiniert oder unbenutzt ist, wie [dokumentiert] (https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx); vielleicht benutzt 'cmd' es intern für etwas ... – aschipfl

+0

Das war mein Gedanke, aber keine der anderen Varianten, die ich probierte, ergab irgendetwas. Es könnte möglich sein, es durch Reverse-Engineering "cmd.exe" zu erarbeiten, aber ich habe nicht diese Art von Zeit. :-) –

Verwandte Themen