7

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

4 Answers 4

9

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

Sign up to request clarification or add additional context in comments.

3 Comments

(one typo PSIsContainere) Yes, I would prefer Where-Object as well. However in the question there are two loops - the inner goes through $exclusionList so the condition should probably be something like -not $($f=$_.Fullname; $exclusionList |?{$f.Contains($_)})
Thanks Richard - can I use an string array for $exclusion. If you look closer at the code you will see that I would have to call get-childitem for every exclusion. This would not perform well if I have a lot of exclusions.
@MarkAllison: -exclude parameter takes a string[] so you can pass multiple wildcards.
3

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

Comments

0

This is ancient. But, I wrote these a while ago to add and remove from powershell lists using recursion. It leverages the ability of powershell to do multiple assignment . That is, you can do $a,$b,$c=@('a','b','c') to assign a b and c to their variables. Doing $a,$b=@('a','b','c') assigns 'a' to $a and @('b','c') to $b.

First is by item value. It'll remove the first occurrence.

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

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

This one removes by index. You can probably mess it up good by passing a value to count in the initial call.

function Remove-IndexFromList ([int]$Index,[array]$List,[array]$chckd_list=@(),[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 )
  }
}

Comments

0

This is a very old question, but the problem is still valid, but none of the answers fit my scenario, so I will suggest another solution.

I my case, I read in an xml configuration file and I want to remove an element from an array.

[xml]$content = get-content $file
$element = $content.PathToArray | Where-Object {$_.name -eq "ElementToRemove" }
$element.ParentNode.RemoveChild($element)

This is very simple and gets the job done.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.