標題: 批處理技術內幕:重定向與句柄 雖然很多人在批處理中經常用到重定向操作符和句柄,但是能理解重定向與句柄高級用法的寥寥無幾,知道重定向與句柄內部實現的更是鳳毛麟角。
按照慣例,先從簡單的說起: >blog.txt echo http:// 重定向輸出操作符>的默認句柄為1,即標準輸出(STDOUT),該代碼將句柄1的輸出重定向到blog.txt文件。 嚴格的說,句柄這個說法并不準確,正確的稱呼應該是文件描述符(file descriptor),但是鑒于幫助文檔ntcmds.chm里面用的也是句柄(handle)這個概念,并且一個文件描述符對應于一個文件句柄,所以本文沿用句柄這個說法。 在進行重定向時,CMD會先調用_get_osfhandle函數獲取文件描述符對應的文件句柄。 如果_get_osfhandle函數的返回值不是-1,即該文件描述符存在對應的文件句柄,則調用_dup函數為該文件描述符所代表的文件創(chuàng)建一個新的文件描述符。 _dup函數返回的是下一個可以使用的文件描述符,由于標準輸入(STDIN)、標準輸出(STDOUT)、標準錯誤(STDERR)已經使用了0、1、2文件描述符,所以不出意外的話,_dup函數應該返回3,這時3和1都代表了標準輸出。這就是那些批處理教程中的所謂的“句柄備份”。 “備份”完句柄之后,CMD會調用_close函數將原來的文件句柄(描述符)關閉。 關閉之后調用CreateFile函數打開重定向操作符后的文件,這里是blog.txt,因為是重定向輸出,所以以只讀模式(GENERIC_WRITE)打開。 成功打開文件之后,又調用_open_osfhandle函數將CreateFile函數返回的文件句柄轉成文件描述符。 因為剛才已經把1給關閉了,所以不出意外的話,_open_osfhanle返回的應該也是1,即句柄(描述符)1指向了blog.txt文件,而不是原來的STDOUT。 到這里為止重定向已經設置好了,剩下的工作就交給echo來處理。echo命令會先判斷句柄1是否指向控制臺(Console),這里顯然不是,所以echo命令調用_os_getfhandle函數來獲取句柄1所對應的文件句柄,然后調用WriteFile函數來把http://寫入blog.txt文件。(這部分不是本文的重點,就不截圖了,詳見《批處理技術內幕:Unicode》。 命令運行完之后,就要恢復重定向的設置,剛才已經把句柄1“備份”到了句柄3,所以CMD調用_dup2函數將句柄1指向句柄3。這就是批處理教程中所謂的“句柄取回”。 從句柄3中取回之后,句柄3就沒用了,于是調用_close函數關閉之。 上面的過程用C代碼簡單的模擬一下,大概是這樣(非常簡略的代碼,CMD的實現要復雜得多): #include <io.h> #include <fcntl.h> #include <stdio.h> #include <windows.h> int main() { int fd; HANDLE hFile; char buf[] = "http://"; DWORD cb; /* 重定向句柄1到blog.txt */ fd = _dup(1); _close(1); hFile = CreateFileW(L"blog.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); _open_osfhandle((long)hFile, _O_WRONLY); /* echo命令 */ WriteFile((HANDLE)_get_osfhandle(1), buf, sizeof(buf) - 1, &cb, NULL); /* 重置重定向 */ _dup2(fd, 1); _close(fd); return 0; } 重定向輸入<與追加模式的重定向輸出>>跟上面差不多,只不過重定向輸入>的話文件是以只讀模式(GENERIC_READ)打開的,而>>在打開文件后還要調用SetFilePointer將文件指針指向文件的末尾。 再簡單說說>&和<&,幫助文檔里的描述讓人摸不著頭腦:<&從后一個句柄讀取輸入并寫入到前一個句柄輸出中;>&將前一個句柄的輸出寫成后一個句柄的輸入。其實除了默認的句柄不同之外,>&和<&是完全一樣的,也就是說2>&1和2<&1的效果相同,都是復制句柄1到句柄2,或者說句柄2指向句柄1所指向的文件。 這樣說還是有些拗口,讓我們看看CMD內部是如何實現的吧。 cd _ 1>error.txt 2>&1 1>error.txt和上面的例子一樣,都不多說了,只講2>&1的部分,先_dup(2)得到一個新的文件描述符4(因為重定向1>error.txt時占用了3),然后_close(2),到這里為止跟上面也是一樣的。 在CMD內部,>&或者<&并不會被當成新的重定向操作符,2>&1中的重定向操作符仍然是>,&1會被認為的重定向到的文件,只不過當文件的第一個字符為&的時候CMD會特別處理罷了。這時CMD會調用dup2函數,第一個參數為>&(或者<&)后面的數字,第二個參數為>&(或者<&)前面的數字(如果沒有則使用操作符默認的句柄,>為1,<為0)。 也就是句柄2現在指向了句柄1的文件,或者說句柄2復制了句柄1,怎么好理解就怎么理解吧,反正句柄2和句柄1現在是同一個東西。 運用重定向與句柄的高級技巧,可以實現一些有用的功能,比如說防止批處理重復運行: @echo off 2>con 3>&2 4>>%0 echo single instance batch echo http:// pause 拓展閱讀: 隨機文章: 這篇文章發(fā)布于 2012年08月26日,星期日,16:55,歸類于 逆向調試。 您可以跟蹤這篇文章的評論通過 RSS 2.0 feed。 您可以留下評論,或者從您的站點trackback。 |
|