Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove item from an array in PowerShell?

I'm using Powershell 1.0 to remove an item from an Array. Here's my script:

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!"}
    }
}

I realize that Get-ChildItem in powershell returns a System.Array type. I therefore get this error when trying to use the Remove method:

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

What I'd like to do is convert $filesToDelete to an ArrayList and then remove items using ArrayList.Remove. Is this a good idea or should I directly manipulate $filesToDelete as a System.Array in some way?

Thanks

like image 594
Mark Allison Avatar asked Feb 26 '10 11:02

Mark Allison


2 Answers

The best way to do this is to use Where-Object to perform the filtering and use the returned array.

You can also use @splat to pass multiple parameters to a command (new in V2). If you cannot upgrade (and you should if at all possible, then just collect the output from Get-ChildItems (only repeating that one CmdLet) and do all the filtering in common code).

The working part of your script becomes:

$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 PSH arrays are immutable, you cannot modify them, but it very easy to create a new one (operators like += on arrays actually create a new array and return that).

like image 70
Richard Avatar answered Sep 29 '22 21:09

Richard


I agree with Richard, that Where-Object should be used here. However, it's harder to read. What I would propose:

# 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

Also note that generally it is not possible to iterate over a collection and change it. You would get an exception.

$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
like image 29
stej Avatar answered Sep 29 '22 23:09

stej