2017-02-21 43 views
6

當我嘗試在下面的例子中多個文件重定向到多個流一樣,一切都按預期工作:爲什麼我不能按照這個順序重定向到兩個輸入流`0 <`和`3 <`?

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

類似的東西的作品也有更多的流和文件,比echo/其他命令,甚至功能代碼塊。即使使用保留的輸出流12似乎也能正常工作。

但是,只要我做,與給定的順序如下流:

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

託管命令提示符實例被立即關閉。

當我改變流的順序,它再次精品工程:

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

那麼,爲什麼cmd實例意外終止,當我嘗試輸入重定向到流的順序03

我正在使用Windows 7(x64)。


當我通過cmd打開一個新的命令提示符實例之前,我嘗試執行上述故障輸入重定向:

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

我可以讀取出現錯誤信息 - 假設文本文件包含了基本名稱後跟行斷:

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

當我這樣做,我可以看到,這兩個文件實際上是重定向:

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

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

相應的輸出:

stream0 

和:

stream3 

這到底是怎麼回事?

回答

4

note:這是對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重定向到file1。有必要保存當前句柄,以便以後能夠恢復它。對於此操作,使用_dup()函數。它將使用最小的可用文件描述符(上表中的流3)爲傳遞的文件描述符(代碼中的流0)創建一個別名。保存操作老手柄收盤後的情況是

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 

現在,一旦重定向已被刪除&0把手指向file2stdin相同的操作與第二重定向

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

完成流存儲在&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 /pfile2讀取,而在第二次測試,試圖從&3閱讀可以到達stdin流。

+0

哇,優秀的答案,+1!這導致我另一個失敗的場景:'3 stream1.txt 3> stream3.txt echo /'('cmd'掛起,直到輸入'exit'),或者2> stream2.txt 3> stream3.txt echo /'(重定向的句柄'2'似乎沒有正確關閉);它看起來像內部過程是相同的輸入和輸出重定向... – aschipfl

+0

所以我認爲我可以說,作爲一項規則:*»預定義的手柄'0' /'1' /'2'重定向後,不要爲同一個命令行/塊使用下一個空閒句柄(既不用於輸入也不用於輸出重定向)。«* – aschipfl

+2

@aschipfl,輸入和輸出重定向在同一個函數中處理,在同一個循環中,它只佔手柄更換。主要區別僅在於新手柄的檢索方式。在你的'1> stream1.txt 3> stream3中。txt echo /'case,'cmd'不會被絞死,只是'stdout'被髮送到'stream3.txt'。一般規則是定義從較高到較低流的重定向(如果可能)。 –

3

看起來像命令完成後流被恢復的方式存在一個錯誤。請看下面的批處理文件:

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

findstr . 

<CON pause 

第一findstr將輸出如預期的stream0.txt內容。

第二個findstr會意外輸出stream3.txt的內容,表明流0意外地被重定向。

當批處理文件完成時,運行它的命令shell也將退出,因爲它在現在是標準輸入流的文件上看到文件結尾。


重試的我在MC ND's answer光實驗之一,我現在可以複製比自流3以外的流了同樣的問題考慮test7.cmd

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

findstr . 

:done 
<CON pause 

運行方式cmd /c test7cmd /c test7 3< other.txt它不會表現出意外的行爲,但它可以運行爲cmd /c "test7 3< other.txt"。 (這是一個facepalm時刻;如果我仔細考慮過,我可能已經發現了這一點,顯然最初的重定向必須在運行批處理腳本的相同命令shell的上下文中。)

所以原因不是「重定向流3以及流0」,而是「重定向第一空閒流以及流0」。 :-)

+0

發現不錯,+1!對我來說問題是,爲什麼這隻發生在流3?在我看來,第3流沒有完全未定義或未被使用[https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx];也許'cmd'在內部使用它的東西... – aschipfl

+0

這是我的想法,但我試過的其他變化沒有發現任何東西。有可能通過對'cmd.exe'進行逆向工程來解決這個問題,但我沒有那種時間。 :-) –

相關問題