2010-02-26 165 views
5

我使用Powershell 1.0從數組中刪除項目。這是我的腳本:如何從PowerShell中的數組中刪除項目?

param (
    [string]$backupDir = $(throw "Please supply the directory to housekeep"), 
    [int]$maxAge = 30, 
    [switch]$NoRecurse, 
    [switch]$KeepDirectories 
    ) 

$days = $maxAge * -1 

# do not delete directories with these values in the path 
$exclusionList = Get-Content HousekeepBackupsExclusions.txt 

if ($NoRecurse) 
{ 
    $filesToDelete = Get-ChildItem $backupDir | where-object {$_.PsIsContainer -ne $true -and $_.LastWriteTime -lt $(Get-Date).AddDays($days)} 
} 
else 
{ 
    $filesToDelete = Get-ChildItem $backupDir -Recurse | where-object {$_.PsIsContainer -ne $true -and $_.LastWriteTime -lt $(Get-Date).AddDays($days)} 
} 

foreach ($file in $filesToDelete) 
{  
    # remove the file from the deleted list if it's an exclusion 
    foreach ($exclusion in $exclusionList) 
    { 
     "Testing to see if $exclusion is in " + $file.FullName 
     if ($file.FullName.Contains($exclusion)) {$filesToDelete.Remove($file); "FOUND ONE!"} 
    } 
} 

我意識到,PowerShell中的Get-ChildItem返回一個System.Array類型。

Method invocation failed because [System.Object[]] doesn't contain a method named 'Remove'. 

我想要做的是$ filesToDelete轉換到一個ArrayList,然後用ArrayList.Remove刪除項目:嘗試使用Remove方法時,因此,我得到這個錯誤。這是一個好主意,還是應該以某種方式直接操作$ filesToDelete作爲System.Array?

感謝

回答

8

做到這一點,最好的方法是使用Where-Object進行過濾,並使用返回的數組。

您也可以使用@splat將多個參數傳遞給一個命令(V2中的新增功能)。如果你不能升級(並且你應該儘可能的話,那麼就從Get-ChildItems收集輸出(只重複一個CmdLet),然後用通用代碼進行所有過濾)。

腳本的工作部分變爲:

$moreArgs = @{} 
if (-not $NoRecurse) { 
    $moreArgs["Recurse"] = $true 
} 

$filesToDelete = Get-ChildItem $BackupDir @moreArgs | 
       where-object {-not $_.PsIsContainer -and 
           $_.LastWriteTime -lt $(Get-Date).AddDays($days) -and 
           -not $_.FullName.Contains($exclusion)} 

在PSH數組是不可變的,你不能修改,但它很容易地創建一個新的(像+=對數組運​​營商實際上是創建一個新的數組並返回)。

+1

(一個錯字'PSIsContainere')是的,我更喜歡'Where-Object'。但是在這個問題中有兩個循環 - 內部通過'$ exclusionList',所以條件應該是'-not $($ f = $ _。Fullname; $ exclusionList |?{$ f.Contains($ _ )})' – stej 2010-02-26 11:47:56

+0

感謝理查德 - 我可以使用字符串數組作$排除。如果仔細觀察代碼,您會看到我必須爲每個排除項都調用get-childitem。如果我有很多排除,這將不會很好。 – 2010-02-26 11:48:47

+0

@stej:將更正 – Richard 2010-02-26 14:41:31

3

我同意理查德認爲應該在這裏使用Where-Object。但是,閱讀起來很困難。 我建議什麼:

# get $filesToDelete and #exclusionList. In V2 use splatting as proposed by Richard. 

$res = $filesToDelete | % { 
    $file = $_ 
    $isExcluded = ($exclusionList | % { $file.FullName.Contains($_) }) 
    if (!$isExcluded) { 
     $file 
    } 
} 

#the files are in $res 

還要注意,一般是不可能遍歷集合,並改變它。你會得到一個例外。

$a = New-Object System.Collections.ArrayList 
$a.AddRange((1,2,3)) 
foreach($item in $a) { $a.Add($item*$item) } 

An error occurred while enumerating through a collection: 
At line:1 char:8 
+ foreach <<<< ($item in $a) { $a.Add($item*$item) } 
    + CategoryInfo   : InvalidOperation: (System.Collecti...numeratorSimple:ArrayListEnumeratorSimple) [], RuntimeException 
    + FullyQualifiedErrorId : BadEnumeration 
0

這是古老的。但是,我前一段時間寫了這些,使用遞歸從powershell列表中添加和刪除。它利用powershell的能力來做multiple assignment。也就是說,你可以做$a,$b,[email protected]('a','b','c')來給它們的變量分配一個b和c。做$a,[email protected]('a','b','c')分配'a'$a@('b','c')$b

首先是按項目值。它會刪除第一個事件。

function Remove-ItemFromList ($Item,[array]$List(throw"the item $item was not in the list"),[array][email protected]()) 
{ 

if ($list.length -lt 1) { throw "the item $item was not in the list" } 

$check_item,$temp_list=$list 
if ($check_item -eq $item) 
    { 
     $chckd_list+=$temp_list 
     return $chckd_list 
    } 
else 
    { 
    $chckd_list+=$check_item 
    return (Remove-ItemFromList -item $item -chckd_list $chckd_list -list $temp_list) 
    } 
} 

這一個由索引移除。你可以通過在初始調用中傳遞一個值來計算好壞。

function Remove-IndexFromList ([int]$Index,[array]$List,[array][email protected](),[int]$count=0) 
{ 

if (($list.length+$count-1) -lt $index) 
    { throw "the index is out of range" } 
$check_item,$temp_list=$list 
if ($count -eq $index) 
    { 
    $chckd_list+=$temp_list 
    return $chckd_list 
    } 
else 
    { 
    $chckd_list+=$check_item 
    return (Remove-IndexFromList -count ($count + 1) -index $index -chckd_list $chckd_list -list $temp_list) 
    } 
}