примечание: Это упрощение, что происходит внутри cmd
когда перенаправлены команда.
Давайте начнем с указанной командой
0< file1 3< file2 echo/
Команды разобранной и представлением необходимых перенаправлений создаются в памяти, какое-то таблицы/список, который будет содержать информацию о перенаправлении: которые обрабатывают перенаправляются, старая сохраненной ручка, где ручка должна указывать при перенаправлении ...
Redirection requests
-------------------------------
redirect saved redirectTo
+--------+--------+------------
R1 | 0 file1
|
R2 | 3 file2
на данный момент (после разбора команды) потока не был изменен.
Существует также системная таблица, в которой обрабатывается каждый дескриптор файла (в нашем случае - потоки cmd
).
File descriptors
------------------
points to
+-----------------
0 | stdin
1 | stdout
2 | stderr
3 |
4 |
примечание Это не совсем верно, основная структура является немного более сложным, но таким образом, легче увидеть, как это работает
Когда команда будет выполнена , вызывается внутренняя функция SetRedir
. Он выполняет итерацию по предыдущей таблице запросов перенаправления, сохраняя существующие дескрипторы и создавая необходимые новые. Начальное состояние
Redirection requests File descriptors
------------------------------- ------------------
redirect saved redirectTo points to
+--------+--------+------------ +-----------------
R1 | 0 file1 0 | stdin
| 1 | stdout
R2 | 3 file2 2 | stderr
3 |
4 |
Первый элемент из запроса перенаправления таблицы (R1) извлекается, чтобы запросы перенаправления потока от 0 до файла1. Необходимо сохранить текущий дескриптор, чтобы впоследствии восстановить его. Для этой операции используется функция _dup()
. Он создаст псевдоним для переданного дескриптора файла (поток 0 в нашем коде), используя наименьший доступный дескриптор файла (поток 3 в предыдущей таблице). После операции сохранения и старые ручки близко ситуация
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 |
После сохранения Перенаправления завершается открытием требуемого файла и ассоциировании открытого дескриптора файла в таблице дескрипторов файлов.В этом случае функция _dup2()
обрабатывает операцию
_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 |
Первого Перенаправление было сделано. Пришло время выполнить ту же операцию с второй. Сначала сохраните старый дескриптор, используя функцию _dup()
. Это позволит связать требуемый дескриптор файла (3) с самой низкой доступной дескриптора (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 <<--/
Перенаправление завершается путем открытия входного файла и связывая его с дескриптора файла
_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
Перенаправление и команда выполняется с потоком 0, перенаправленным на file1
, и поток 3 перенаправляется на file2
.
Как только это сделано, пришло время вернуть процесс. ResetRedir()
функция обрабатывает операцию. Он снова использует функцию _dup2()
для переноса сохраненного дескриптора в исходный дескриптор файла. Здесь возникает проблема, как сохраненный дескриптор был изменен
_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
Теперь та же операция выполняется со вторым перенаправлением
_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 | ---/
После того, как перенаправление было удалено с &0
ручки указывает на file2
и stdin
поток хранится в &3
. Это можно проверить, как
@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 !.!"
Это будет генерировать
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:\>
можно видеть, что в первом тесте set /p
прочитал от file2
, и во втором тесте, пытаясь прочитать от &3
поток stdin
может быть достигнут.
Ничего себе, отличный ответ, +1 !! Это привело меня к другому сценарию, который терпит неудачу: '3 stream1.txt 3> stream3.txt echo /' ('cmd' зависает, пока я не войду в' exit'), или '2> stream2.txt 3> stream3.txt echo/'(перенаправленный дескриптор' 2', похоже, не закрывается должным образом); похоже, что внутренний процесс одинаковый как для перенаправления ввода, так и вывода ... –
aschipfl
Итак, я думаю, что могу сказать, что: * »После перенаправления предопределенного дескриптора' 0'/'1' /' 2', не используйте следующую свободную рукоятку (ни для ввода, ни для перенаправления вывода) для той же командной строки/блока. «* – aschipfl
@aschipfl, перенаправление ввода и вывода обрабатывается внутри одной и той же функции внутри одного и того же цикла, это просто учет замените ручку. Основное отличие заключается только в том, как извлекается новый дескриптор. В вашем '1> stream1.txt 3> stream3.txt echo/'case,' cmd' не вешается, просто 'stdout' отправляется в' stream3.txt'. Общее правило заключается в определении перенаправления (когда это возможно) от более высоких к более низким потокам. –