Remove random items from `Get-ChildItem` output

Viewed 61

I'd like to ask a question about removing lines from a variable that has strings obtained from get-childitem. Right now, I'm extracing lines indicating files with this :

$FolderToOpen= Get-ChildItem -Path $SrcDir -Filter *.$ExtLst

That gets the files from a specified folder and specified extensions Then, I get 4 random lines from that variable

$FourFiles = $FolderToOpen | Get-Random -Count 4

But then, here's my problem, I'd like to delete those 4 lines from the first variable. However I can't get to do that, doing

$FolderToOpen -replace "$FourFiles",""

for exemple doesn't do anything. Could someone help me please? Thank you in advance :)

4 Answers

The simplest way of doing that is to take advantage of Compare-Object which compares arrays element-wise:

Compare-Object -ReferenceObject $FolderToOpen -DifferenceObject $FourFiles -PassThru

The problem with your approach is that PowerShell prefers operating on objects instead of strings. The output of Get-ChildItem is a stream of objects, what can be proven by piping it to Get-Member:

PS> dir | Get-Member 
TypeName: System.IO.DirectoryInfo
...
TypeName: System.IO.FileInfo
...

Since you are interested in particularly filtering strings, you can cast all the objects to strings beforehand and then perform filtering:

$FourFiles = $FolderToOpen | Get-Random -Count 4 | % {$_.ToString()}
$FolderToOpen | Where {$_.ToString() -inotin $FourFiles}

Note that $FolderToOpen is a stream of strings, instead of one big string like you would have in Bash. Therefore I used Where to filter the entries.

The Get-ChildItem cmdlet returns an array of objects by default and as such is of a fixed size (see Everything you wanted to know about arrays for further information on this)

If we cast the results of the Get-ChildItem call to a list first, we'll then be able to add and remove items (without the need to create new arrays to hold the updated data)

Updated code below should give you what you're looking for:

# Call Get-ChildItem but store the results as a list of objects
# rather than a standard array of objects
[Collections.Generic.List[Object]]$FolderToOpen= Get-ChildItem -Path $SrcDir -Filter *.$ExtLst

# Get your random selection of 4 files
$FourFiles = $FolderToOpen | Get-Random -Count 4

# Remove them from the list 
$FolderToOpen.RemoveAll({ param($folder) $folder -in $FourFiles })

The RemoveAll function above will return the total number of entries removed from the list (in this example, 4).

A solution that uses only Get-Random. Basically use Get-Random to select the files to include, instead of exclude. Then you don't need to remove anything. The output of Get-Random is already what you want.

$FolderToOpen = @(Get-ChildItem -Path $SrcDir -Filter *.$ExtLst)

$includeCount = $FolderToOpen.Count - 4
$FolderToOpen = if( $includeCount -gt 0 ) { 
    $FolderToOpen | Get-Random -Count $includeCount 
}
  • To make sure we always get an array out of Get-ChildItem, use the array sub-expression operator @(). Otherwise you'd get a single object, if Get-ChildItem outputs only a single file.
  • The if statement is required to make sure we don't pass an invalid -Count argument to Get-Random, when Get-ChildItems returns four or less files.
  • Note that this outputs the files in random order, but you could easily sort them by path again:
    $FolderToOpen | Get-Random -Count $includeCount | Sort-Object FullName
    
Related