Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine the results of two distinct Get-ChildItem calls into single variable to do the same processing on them

I'm trying to write a PowerShell script to build a list of files, from several directories. After all directories have been added to the main list, I'd like to do the same processing on all files.

This is what I have:

$items = New-Object Collections.Generic.List[IO.FileInfo]

$loc1 = @(Get-ChildItem -Path "\\server\C$\Program Files (x86)\Data1\" -Recurse)
$loc2 = @(Get-ChildItem -Path "\\server\C$\Web\DataStorage\" -Recurse)

$items.Add($loc1) # This line fails (the next also fails)
$items.Add($loc2)

# Processing code is here

which fails with this error:

Cannot convert argument "0", with value: "System.Object[]", for "Add" to type "System.IO.FileInfo": "Cannot convert the "System.Object[]" va lue of type "System.Object[]" to type "System.IO.FileInfo"."

I am mostly interested in what is the correct approach for this type of situation. I realize that my code is a very C way of doing it -- if there is a more PowerShell way to acomplish the same task, I'm all for it. The key, is that the number of $loc#'s may change over time, so adding and removing one or two should be easy in the resulting code.

like image 292
Nate Avatar asked Jan 20 '11 16:01

Nate


4 Answers

Not sure you need a generic list here. You can just use a PowerShell array e.g.:

$items  = @(Get-ChildItem '\\server\C$\Program Files (x86)\Data1\' -r)
$items += @(Get-ChildItem '\\server\C$\Web\DataStorage\' -r)

PowerShell arrays can be concatenated using +=.

like image 182
Keith Hill Avatar answered Oct 07 '22 23:10

Keith Hill


From get-help get-childitem: -Path Specifies a path to one or more locations. Wildcards are permitted. The default location is the current directory (.).

$items = get-childitem '\\server\C$\Program Files (x86)\Data1\','\\server\C$\Web\DataStorage\' -Recurse
like image 39
mjolinor Avatar answered Oct 07 '22 23:10

mjolinor


Here is some perhaps even more PowerShell-ish way that does not need part concatenation or explicit adding items to the result at all:

# Collect the results by two or more calls of Get-ChildItem
# and perhaps do some other job (but avoid unwanted output!)
$result = .{

    # Output items
    Get-ChildItem C:\TEMP\_100715_103408 -Recurse

    # Some other job
    $x = 1 + 1

    # Output some more items
    Get-ChildItem C:\TEMP\_100715_110341 -Recurse

    #...
}

# Process the result items
$result

But the code inside the script block should be written slightly more carefully to avoid unwanted output mixed together with file system items.

EDIT: Alternatively, and perhaps more effectively, instead of .{ ... } we can use @( ... ) or $( ... ) where ... stands for the code containing several calls of Get-ChildItem.

like image 7
Roman Kuzmin Avatar answered Oct 07 '22 22:10

Roman Kuzmin


Keith's answer is the PowerShell way: just use @(...)+@(...).

If you actually do want a typesafe List[IO.FileInfo], then you need to use AddRange, and cast the object array to a FileInfo array -- you also need to make sure you don't get any DirectoryInfo objects, or else you need to use IO.FileSystemInfo as your list type:

So, avoid directories:

$items = New-Object Collections.Generic.List[IO.FileInfo]
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Program Files (x86)\Data1\' -r | Where { -not $_.PSIsContainer } )) )
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Web\DataStorage\' -r | Where { -not $_.PSIsContainer } )) )

Or use FileSystemInfo (the common base class of FileInfo and DirectoryInfo):

$items = New-Object Collections.Generic.List[IO.FileSystemInfo]
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Program Files (x86)\Data1\' -r)) )
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Web\DataStorage\' -r)) )
like image 5
Jaykul Avatar answered Oct 07 '22 22:10

Jaykul