uwaga: Jest to uproszczenie, co dzieje się wewnątrz cmd
gdy przekierowany komenda jest wykonywana.
Zacznijmy wskazanego polecenia
0< file1 3< file2 echo/
Polecenie jest analizowany i reprezentacji potrzebnych przekierowań jest tworzony w pamięci jakieś tabeli/listy, które przechowuje informacje o przekierowanie: który obsługiwać jest przekierowywany, stary zapisany uchwyt, gdzie uchwyt powinien wskazywać kiedy przekierowany ...
Redirection requests
-------------------------------
redirect saved redirectTo
+--------+--------+------------
R1 | 0 file1
|
R2 | 3 file2
w tym momencie (po parsowania polecenia) nie strumień został zmieniony.
Istnieje również tabela systemowa, która obsługuje, gdzie każdy z deskryptorów plików (w naszym przypadku, strumienie cmd
) naprawdę wskazuje.
File descriptors
------------------
points to
+-----------------
0 | stdin
1 | stdout
2 | stderr
3 |
4 |
uwaga To nie jest do końca prawda, podstawowa struktura jest nieco bardziej skomplikowane, ale w ten sposób łatwiej jest zobaczyć, jak to działa
Gdy komenda zostanie wykonana , wywoływana jest wewnętrzna funkcja SetRedir
. Powtarza on poprzednią tabelę żądań przekierowania, zapisując istniejące uchwyty i tworząc wymagane nowe. Początkowy stan jest
Redirection requests File descriptors
------------------------------- ------------------
redirect saved redirectTo points to
+--------+--------+------------ +-----------------
R1 | 0 file1 0 | stdin
| 1 | stdout
R2 | 3 file2 2 | stderr
3 |
4 |
pierwszy element przekierowanie żądania tabeli (R1) jest pobierany, żądania aby przekierowanie strumienia 0 do pliku1. Konieczne jest zapisanie bieżącego uchwytu, aby później móc go przywrócić. Do tej operacji używana jest funkcja _dup()
. Tworzy alias dla przesłanego deskryptora pliku (strumień 0 w naszym kodzie), używając najmniejszego dostępnego deskryptora pliku (strumień 3 z poprzedniej tabeli). Po operacji zapisu i stare klamki zamknąć sytuacja jest
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 |
Po zapisaniu przekierowanie jest zakończona poprzez otwarcie żądany plik i skojarzenie uchwyt pliku otwartego w tabeli deskryptorów.W tym przypadku funkcja _dup2()
obsługuje operację
_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 |
Pierwszy przekierowanie zostało zrobione. Nadszedł czas, aby wykonać tę samą operację z drugą, . Najpierw zapisz stary uchwyt za pomocą funkcji _dup()
. To powiązanie żądany deskryptora (3) o najniższym dostępnym deskryptora (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 <<--/
Przekierowanie uzupełnia otwarcia pliku wejściowego i kojarzenia go z deskryptora
_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
przekierowania zostało zakończone i polecenie jest wykonywane ze strumieniem 0 przekierowanym do file1
, a strumień 3 przekierowany do file2
.
Po zakończeniu, nadszedł czas, aby przywrócić proces. Funkcja ResetRedir()
obsługuje operację. Ponownie używa funkcji _dup2()
, aby przenieść zapisany uchwyt do oryginalnego deskryptora pliku. Tutaj powstaje problem jak zapisane deskryptor zmieniono
_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
Teraz ta sama operacja jest wykonywana z drugiego przekierowania
_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 | ---/
Gdy przekierowanie zostało usunięte &0
punkty uchwytu do file2
i stdin
strumień jest przechowywany w &3
. To mogą być testowane jako
@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 !.!"
To wygeneruje
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:\>
Można zauważyć, że w pierwszym teście, set /p
zapoznał się z file2
, aw drugim teście, próbując odczytać z &3
można uzyskać strumień stdin
.
Wow, doskonała odpowiedź, +1 !! To doprowadziło mnie do innego scenariusza, który się nie powiódł: '3 stream1.txt 3> stream3.txt echo /' ('cmd' zawiesza się dopóki nie wejdę w' exit') lub '2> stream2.txt 3> stream3.txt echo/'(przekierowany uchwyt' 2' nie jest poprawnie zamknięty); wygląda na to, że wewnętrzny proces jest taki sam dla przekierowania wejścia i wyjścia ... –
aschipfl
Więc myślę, że mógłbym powiedzieć z reguły, że: * »Po przekierowaniu predefiniowanego uchwytu' 0'/'1' /' 2', nie używaj następnej wolnej klamki (ani dla wejścia, ani dla przekierowania wyjścia) dla tego samego wiersza poleceń/bloku. «* – aschipfl
@aschipfl, przekierowanie wejścia i wyjścia jest obsługiwane wewnątrz tej samej funkcji, wewnątrz tej samej pętli, to tylko odpowiada za wymiana uchwytu. Główną różnicą jest tylko sposób pobrania nowego uchwytu. W twoim strumieniu '1> stream1.txt 3> stream3.txt echo/'case,' cmd' nie jest zawieszony, po prostu 'stdout' jest wysyłany do' stream3.txt'. Ogólną zasadą jest definiowanie przekierowań (o ile to możliwe) z wyższych do niższych strumieni. –