2011-08-25 54 views
15

DIRGCI是在PowerShell中慢,但快CMD。有什麼辦法可以加快速度嗎?如何加快Powershell的獲取-Childitem在UNC

輸入cmd.exe,一分一秒的延遲之後,這個快速響應的CMD窗口能跟上

dir \\remote-server.domain.com\share\folder\file*.* 

使用PowerShell(V2),一個40多秒後,這與響應一個明顯的緩慢(可能每秒3-4行)

gci \\remote-server.domain.com\share\folder\file*.* 

我試圖掃描遠程服務器上的日誌,所以也許有更快的方法。

get-childitem \\$s\logs -include $filemask -recurse | select-string -pattern $regex 
+1

cmd.exe中的Dir一直比我曾經使用VBScript中的FileSystemObject或PS中的FileSystem提供程序的任何其他方法都快。甚至到了我已經編寫了VBScript函數來發送到cmd.exe Dir以進行文件搜索的時候。遞歸只是使其指數更明顯。 – EBGreen

+0

也許你的PowerShell可以用像'dir/b/s \\ server \ logs \ file *。*'這樣的命令來調用CMD shell來獲取PowerShell可以輕鬆處理的表單中的完整路徑。 – ewall

+0

@EBGreen - ''recurse'在那裏作爲一個運行反對更本地域的結轉。不是真的需要。無論有沒有它都很慢。我問這個問題的原因是(a)這是一個已知的問題,(b)很難相信PS比舊的CMD糟糕得多。 –

回答

13

Here是一個很好的解釋,爲什麼Get-ChildItem的速度很慢,Lee Holmes。如果您注意到頁面底部的「Anon 11 Mar 2010 11:11 AM」的評論,他的解決方案可能適合您。

匿名的代碼:

# SCOPE: SEARCH A DIRECTORY FOR FILES (W/WILDCARDS IF NECESSARY) 
# Usage: 
# $directory = "\\SERVER\SHARE" 
# $searchterms = "filname[*].ext" 
# PS> $Results = Search $directory $searchterms 

[reflection.assembly]::loadwithpartialname("Microsoft.VisualBasic") | Out-Null 

Function Search { 
    # Parameters $Path and $SearchString 
    param ([Parameter(Mandatory=$true, ValueFromPipeline = $true)][string]$Path, 
    [Parameter(Mandatory=$true)][string]$SearchString 
) 
    try { 
    #.NET FindInFiles Method to Look for file 
    # BENEFITS : Possibly running as background job (haven't looked into it yet) 

    [Microsoft.VisualBasic.FileIO.FileSystem]::GetFiles(
    $Path, 
    [Microsoft.VisualBasic.FileIO.SearchOption]::SearchAllSubDirectories, 
    $SearchString 
    ) 
    } catch { $_ } 

} 
+1

+1。這不會加速GCI通過UNC,但提供了一個快速的解決方法。在大多數情況下甚至比CMD DIR更快 - 在我的答案中查看測試結果。謝謝,我會接受這個答案,如果沒有人儘快與一個更好的人聯繫。 –

+0

好的,接受。謝謝,肖恩。 –

+1

注意:根據.NET版本的不同,GetFiles()的性能可能會以指數級的方式降級大型目錄列表([msdn ps blog]](http://blogs.msdn.com/b/powershell/archive/2009 /11/04/why-is-get-childitem-so-slow.aspx)) – Kyle

14

好吧,這就是我如何做,它似乎工作。

$files = cmd /c "$GETFILESBAT \\$server\logs\$filemask" 
foreach($f in $files) { 
    if($f.length -gt 0) { 
     select-string -Path $f -pattern $regex | foreach-object { $_ } 
    } 
} 

然後$ GETFILESBAT指向此:

@dir /a-d /b /s %1 
@exit 

我寫和刪除PowerShell腳本此BAT文件,所以我想這是一個唯一的PowerShell的解決方案,但它不只使用PowerShell。

我的初步性能指標表明這是更快eleventy萬倍。

我測試了gci與cmd dir與@Shawn Melton引用的FileIO.FileSystem.GetFiles link

底線是,對於本地驅動器上的日常使用,GetFiles是最快的。 到目前爲止CMD DIR是可敬的。一旦你用很多文件引入了一個較慢的網絡連接,CMD DIRGetFiles稍快。然後Get-ChildItem ......哇,這個範圍是不是太糟糕到恐怖,這取決於所涉及的文件數量和連接速度。

一些測試運行。我已經在測試中移動了GCI,以確保結果一致。

10次迭代掃描c:\windows\temp用於* .tmp文件

.\test.ps1 "c:\windows\temp" "*.tmp" 10 
GCI... 00:00:01.1391139 
GetFiles... 00:00:00.0570057 
CMD dir... 00:00:00.5360536 

的GetFiles比CMD目錄,其本身比2倍以上GCI要快10倍。掃描c:\windows\temp爲* .tmp文件的

10次迭代遞推

.\test.ps1 "c:\windows\temp" "*.tmp" 10 -recurse 
GetFiles... 00:00:00.7020180 
CMD dir... 00:00:00.7644196 
GCI... 00:00:04.7737224 

的GetFiles比CMD DIR快一點,而且兩者都比GCI幾乎快7倍。在另一個域掃描現場服務器應用程序日誌文件

.\test.ps1 "\\closeserver\logs\subdir" "appname*.*" 10 
GCI... 00:00:06.0796079 
GetFiles... 00:00:00.3590359 
CMD dir... 00:00:00.6270627 

的GetFiles的

10次迭代大約爲2x比CMD DIR更快,本身10倍比GCI更快。

掃描遠程服務器上的其他域的應用程序日誌文件,有許多文件的一個迭代涉及

.\test.ps1 "\\distantserver.company.com\logs\subdir" "appname.2011082*.*" 
GCI... 00:11:09.5525579 
GetFiles... 00:00:00.4360436 
CMD dir... 00:00:00.3340334 

CMD目錄最快要與許多文件的遠程服務器,但GetFiles的是體面地關閉。另一方面,GCI要慢幾千倍。

兩次迭代掃描遠程服務器上的另一域爲應用程序日誌文件,有許多文件

.\test.ps1 "\\distantserver.company.com\logs\subdir" "appname.20110822*.*" 2 
GetFiles... 00:00:01.4976384 
CMD dir... 00:00:00.9360240 
GCI... 00:22:17.3068616 

更多或更少的作爲測試迭代增加線性增加的。在另一個域掃描遠程服務器的應用程序日誌文件,以較少的文件

.\test.ps1 "\\distantserver.company.com\logs\othersubdir" "appname.2011082*.*" 10 
GCI... 00:00:01.9656630 
GetFiles... 00:00:00.5304170 
CMD dir... 00:00:00.6240200 

這裏GCI是不是太糟糕,的GetFiles的

一個迭代的3倍快,CMD dir是緊隨其後。

結論

GCI需要一個-raw-fast選項不會嘗試做這麼多。與此同時,GetFiles是一個健康的替代品,只是偶爾比CMD dir慢一點,而且通常更快(由於產生CMD.exe?)。

僅供參考,以下是test.ps1代碼。

param ([string]$path, [string]$filemask, [switch]$recurse=$false, [int]$n=1) 
[reflection.assembly]::loadwithpartialname("Microsoft.VisualBasic") | Out-Null 
write-host "GetFiles... " -nonewline 
$dt = get-date; 
for($i=0;$i -lt $n;$i++){ 
    if($recurse){ [Microsoft.VisualBasic.FileIO.FileSystem]::GetFiles($path, 
     [Microsoft.VisualBasic.FileIO.SearchOption]::SearchAllSubDirectories,$filemask 
    ) | out-file ".\testfiles1.txt"} 
    else{ [Microsoft.VisualBasic.FileIO.FileSystem]::GetFiles($path, 
     [Microsoft.VisualBasic.FileIO.SearchOption]::SearchTopLevelOnly,$filemask 
    ) | out-file ".\testfiles1.txt" }} 
$dt2=get-date; 
write-host $dt2.subtract($dt) 
write-host "CMD dir... " -nonewline 
$dt = get-date; 
for($i=0;$i -lt $n;$i++){ 
    if($recurse){ 
    cmd /c "dir /a-d /b /s $path\$filemask" | out-file ".\testfiles2.txt"} 
    else{ cmd /c "dir /a-d /b $path\$filemask" | out-file ".\testfiles2.txt"}} 
$dt2=get-date; 
write-host $dt2.subtract($dt) 
write-host "GCI... " -nonewline 
$dt = get-date; 
for($i=0;$i -lt $n;$i++){ 
    if($recurse) { 
    get-childitem "$path\*" -include $filemask -recurse | out-file ".\testfiles0.txt"} 
    else {get-childitem "$path\*" -include $filemask | out-file ".\testfiles0.txt"}} 
$dt2=get-date; 
write-host $dt2.subtract($dt) 
+0

+1「十一千」 – x0n

+0

任何完整的源代碼示例? – Kiquenet

+1

@Kiquenet - 你是什麼意思?答案最後有完整的資料來源。 –

0

我嘗試了一些建議的方法有大量的文件(190.000〜)。正如凱爾的評論中提到的,GetFiles在這裏並不是很有用,因爲它幾乎是永遠需要的。

cmd dir在我的第一次測試中優於Get-ChildItems,但是看起來,如果使用-Force參數,GCI會加速很多。有了這個,所需的時間與cmd目錄大致相同。

P.S .:在我的情況下,我不得不排除大部分文件,因爲它們的擴展名。這是用gci中的-Exclude和其他命令中的|進行的。因此,僅搜索文件的結果可能略有不同。

相關問題