2010-02-26 5 views
5

Sto usando Powershell 1.0 per rimuovere un elemento da una matrice. Ecco il mio script:Come rimuovere un elemento da un array in 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!"} 
    } 
} 

mi rendo conto che Get-ChildItem in PowerShell restituisce un tipo System.Array. Ho quindi ottengo questo errore quando si tenta di utilizzare il metodo Remove:

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

Quello che mi piacerebbe fare è convertire $ filesToDelete a un ArrayList e quindi rimuovere gli elementi utilizzando ArrayList.Remove. È una buona idea o dovrei manipolare direttamente $ filesToDelete come System.Array in qualche modo?

Grazie

risposta

8

Il modo migliore per farlo è usare Where-Object per eseguire il filtraggio e utilizzare la matrice restituita.

È inoltre possibile utilizzare @splat per passare più parametri a un comando (nuovo in V2). Se non è possibile eseguire l'aggiornamento (e se necessario, è sufficiente raccogliere l'output da Get-ChildItems (solo ripetendo quello CmdLet) e fare tutto il filtraggio nel codice comune).

La parte di lavoro dello script diventa:

$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)} 

In array PSH sono immutabili, non è possibile modificare, ma è molto facile creare una nuova (operatori come += su array in realtà creare un nuovo array e restituiscilo).

+1

(un typo 'PSIsContainere') Sì, preferirei anche" Dove-Oggetto ". Comunque nella domanda ci sono due cicli - l'interno passa attraverso '$ exclusionList' quindi la condizione dovrebbe probabilmente essere qualcosa come' -not $ ($ f = $ _. Fullname; $ exclusionList |? {$ F.Contains ($ _)}) – stej

+0

Grazie Richard, posso usare una serie di stringhe per $ esclusione. Se osservi attentamente il codice, vedrai che dovrei chiamare get-childitem per ogni esclusione. Questo non funzionerebbe bene se ho molte esclusioni. –

+0

@stej: Correggerà il parametro – Richard

3

Sono d'accordo con Richard, che Where-Object dovrebbe essere usato qui. Tuttavia, è più difficile da leggere. Quello che vorrei proporre:

# 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 

Si noti inoltre che in genere non è possibile iterare su una collezione e di modificarlo. Avresti un'eccezione.

$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

Questo è antico. Ma, ho scritto questi un po 'di tempo fa per aggiungere e rimuovere dagli elenchi di PowerShell utilizzando la ricorsione. Sfrutta la capacità di PowerShell di fare multiple assignment. Cioè, puoi fare $a,$b,[email protected]('a','b','c') per assegnare un bec alle loro variabili. Fare $a,[email protected]('a','b','c') assegna 'a' a $a e @('b','c') a $b.

Il primo è in base al valore dell'articolo. Rimuoverà la prima occorrenza.

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) 
    } 
} 

Questo rimuove per indice. Probabilmente si può incasinare bene passando un valore per contare nella chiamata iniziale.

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) 
    } 
}