0

我使用File::Find來遍歷目錄樹和Win32::FileGetAttributes函數來查看在其中找到的文件的屬性。這工作在一個單線程程序。GetAttributes在子線程中使用錯誤的工作目錄

然後我把目錄遍歷到一個單獨的線程,它停止工作。 GetAttributes在「系統找不到指定的文件」的每個文件上都失敗,作爲$^E中的錯誤消息。

我將問題追溯到File::Find使用chdir這一事實,並且顯然GetAttributes未使用當前目錄。我可以通過傳遞一個絕對路徑來解決這個問題,但是然後我可以運行到路徑長度限制,並且在腳本運行的地方肯定會出現長路徑,所以我真的需要利用chdir和相對路徑。

爲了說明問題,這裏是它會在當前目錄中的文件的腳本,在子目錄另一個文件,CHDIR對子目錄,並查找文件3種方式:system("dir")openGetAttributes

當腳本不帶參數運行,dir顯示子目錄,open發現在子目錄中的文件,並GetAttributes成功返回其屬性。當與--thread一起運行時,所有測試均以子線程完成,並且diropen仍然有效,但GetAttributes失敗。然後它調用GetAttributes對原始目錄(我們已經chdir'ed了)的文件,它找到了一個!不知怎的,GetAttributes正在使用進程的原始工作目錄 - 或者可能是主線程的工作目錄 - 與所有其他文件操作不同。

我該如何解決這個問題?我可以保證主線程不會做任何chdir'ing,如果這很重要。

use strict; 
use warnings; 

use threads; 
use Data::Dumper; 
use Win32::File qw/GetAttributes/; 
sub doit 
{ 
    chdir("testdir") or die "chdir: $!\n"; 
    system "dir"; 
    my $attribs; 
    open F, '<', "file.txt" or die "open: $!\n"; 
    print "open succeeded. File contents:\n-------\n", <F>, "\n--------\n"; 
    close F; 
    my $x = GetAttributes("file.txt", $attribs); 
    print Dumper [$x, $attribs, $!, $^E]; 
    if(!$x) { 
    # If we didn't find the file we were supposed to find, how about the 
    # bad one? 
    $x = GetAttributes("badfile.txt", $attribs); 
    if($x) { 
     print "GetAttributes found the bad file!\n"; 
     if(open F, '<', "badfile.txt") { 
     print "opened the bad file\n"; 
     close F; 
     } else { 
     print "But open didn't open it. Error: $! ($^E)\n"; 
     } 
    } 
    } 
} 

# Setup 
-d "testdir" or mkdir "testdir" or die "mkdir testdir: $!\n"; 
if(!-f "badfile.txt") { 
    open F, '>', "badfile.txt" or die "create badfile.txt: $!\n"; 
    print F "bad\n"; 
    close F; 
} 
if(!-f "testdir/file.txt") { 
    open F, '>', "testdir/file.txt" or die "create testdir/file.txt: $!\n"; 
    print F "hello\n"; 
    close F; 
} 

# Option 1: do it in the main thread - works fine 
if(!(@ARGV && $ARGV[0] eq '--thread')) { 
    doit(); 
} 

# Option 2: do it in a secondary thread - GetAttributes fails 
if(@ARGV && $ARGV[0] eq '--thread') { 
    my $thr = threads->create(\&doit); 
    $thr->join(); 
} 

回答

0

最後,我想通了,是用perl保持某種只適用於perl的內置運營商二次CWD,而GetAttributes是使用原生的CWD。我不知道它爲什麼這樣做,或者爲什麼它只發生在輔助線程中;我最好的猜測是,perl試圖模擬每個進程的一個cwd的unix規則,並且因爲模塊不兼容而失敗。

不管是什麼原因,有可能迫使本土CWD是一樣的Perl的CWD,每當你即將做Win32::*操作,這樣來解決它:

use Cwd; 
use Win32::FindFile qw/SetCurrentDirectory/; 

... 

SetCurrentDirectory(getcwd()); 

按理說File::Find應在Win32上運行時執行此操作。

當然這隻會讓「路徑名太長」的問題變得更糟,因爲現在您訪問的每個目錄都將成爲絕對路徑的目標,即SetCurrentDirectory;嘗試通過一系列較小的SetCurrentDirectory調用來解決此問題,並且您必須找出一種方法來找回來自哪裏,當您甚至沒有fchdir時很難。