Why can't I redirect to two input streams `0<` and `3<` in that order?

前端 未结 2 1530
一生所求
一生所求 2021-01-02 10:35

When I try to redirect multiple files into multiple streams, like in the following example, everything works as expected:

3< stream3.txt 4< stream4.txt         


        
2条回答
  •  礼貌的吻别
    2021-01-02 11:36

    note: This is a simplification of what happens inside cmd when the redirected command is executed.

    Let's start with the indicated command

    0< file1  3< file2 echo/
    

    The command is parsed and a representation of the needed redirections is created in memory, some kind of table/list that will hold the information about the redirection: which handle is redirected, the old saved handle, to where the handle should point when redirected, ...

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

    At this point (after parsing the command) no stream has been changed.

    There is also a system table that handles where each of the file descriptors (in our case, the cmd streams) really points.

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

    note This is not exactly true, the underlying structure is a little more complex, but that way it is easier to see how it works

    When the command is going to be executed, the internal SetRedir function is called. It iterates over the previous redirection request table saving existing handles and creating required new ones. The initial state is

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

    First element from redirection request table (R1) is retrieved, the requests to redirect stream 0 to file1. It is necessary to save the current handle to later be able to restore it. For this operation the _dup() function is used. It will create an alias for the passed file descriptor (stream 0 in our code), using the smallest available file descriptor (stream 3 in previous table). After save operation and old handle close the situation is

       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 |
    

    Once saved, the redirection is completed by opening the requested file and associating open file handle in the file descriptors table. In this case the _dup2() function handles the operation

     _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 |
    

    The first redirection has been done. It is time to do the same operation with the second one. First, save the old handle using the _dup() function. This will associate the requested file descriptor (3) with the lowest available descriptor (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  <<--/
    

    The redirection is completed by opening the input file and associating it with the file descriptor

    _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
    

    The redirection has been completed and the command is executed with the stream 0 redirected to file1 and the stream 3 redirected to file2.

    Once done, it's time to revert the process. ResetRedir() function handles the operation. It uses again the _dup2() function to transfer the saved handle to the original file descriptor. Here the problem arises as the saved descriptor was changed

    _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
    

    Now, the same operation is done with the second redirection

    _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 |         ---/
    

    Once the redirection has been removed the &0 handle points to file2 and the stdin stream is stored in &3. This can be tested as

    @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 !.!"
    

    That will generate

    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:\>
    

    It can be seen that in the first test, the set /p has read from file2, and in the second test, trying to read from &3 the stdin stream can be reached.

提交回复
热议问题