3

From powershell, ls -R *.txt will list files recursively by directory, or, even better:

PS> Get-ChildItem -Path C:\Test -Name

logs

anotherfile.txt
Command.txt
CreateTestFile.ps1
ReadOnlyFile.txt

but how do I feed this into an array? I would like an array of the file (?) object itself, looking at:

Get-ChildItem "C:\WINDOWS\System32" *.txt -Recurse | Select-Object FullName

https://stackoverflow.com/a/24468733/262852

I'm looking for an array of "file" objects with powershell from these types of commands.

probably better syntax:

Copy-Item -Filter *.txt -Path c:\data -Recurse -Destination C:\temp\text

but rather than copy the items, I just want an object, or rather, array of objects. Not the path to a file, not the file, but, presumably, a powershell reference or pointer to a file.

(Reading the fine manual now.)

mklement0
  • 382,024
  • 64
  • 607
  • 775
Thufir
  • 8,216
  • 28
  • 125
  • 273
  • 5
    `get-ChildItem "C:\windows\system32" -include "*.txt" -Recurse` produces an array of objects. – AdminOfThings Apr 19 '19 at 16:47
  • 4
    I am not sure what you are getting at here. But powershell automatically returns an object. All you have to do is assign it to a variable. `$txtfiles = Get-ChildItem "C:\WINDOWS\System32" *.txt -Recurse | Select-Object FullName`. In this case, `$txtfiles` will be an array object. – Sid Apr 19 '19 at 16:50
  • @AdminOfThings the array will contain objects of type `System.IO.FileSystemInfo`? Okay, that was what I needed to know. – Thufir Apr 19 '19 at 23:03

2 Answers2

7

tl;dr

  • When you capture a PowerShell statement's output in a variable (e.g.,
    $output = Get-ChildItem ...), it is automatically collected in an array if there are two or more output objects.

  • To ensure that an array is always used - even with only a single or no output object - use @(...) (e.g.,
    $output = @(Get-ChildItem ...))


  • PowerShell cmdlets, such as Get-ChildItem, can output any number of objects.

    • Get-ChildItem outputs [System.IO.FileInfo] and/or [System.IO.DirectoryInfo] objects, depending on whether information about files and/or directories is being output.

    • To determine a given cmdlet's output-object types:

      • Run, e.g., (Get-Command Get-ChildItem).OutputType
      • If that doesn't work, or to see what types are output for a specific invocation, use
        Get-ChildItem | Get-Member.
      • Get-Help -Full Get-ChildItem should show an OUTPUTS section as well, as does the online help, though not that in the case of Get-ChildItem it is less specific, since Get-ChildItem also works with providers other than the filesystem.
  • When output to the pipeline, each output object is passed individually to the next command in the pipeline for typically immediate processing.

  • When output is captured in a variable ($var = ...), the following logic applies:

    • If two or more objects are output, they are collected in a regular PowerShell array, which is of type [object[]] (even though the actual elements have specific types).
    • If one object is output, it is output as-is; that is, it is not wrapped in an array.
    • If no objects are output, an "array-valued null" is output (sometimes called "AutomationNull" for its type name), which in expression contexts behaves like $null and in enumeration contexts like an empty collection; it results in no visible output - see this answer for details.

Therefore, when captured in a variable, a given command may situationally return:

  • an array of objects
  • a single object
  • "nothing" ([System.Management.Automation.Internal.AutomationNull]::Value)

To ensure that a given command's output is always treated as an array, you have two options:

  • Use @(...), the array subexpression operator; e.g.

    • $fileSystemObjects = @(Get-ChildItem -Recurse -Filter *.txt)
  • Type-constrain the target variable with [array] (which is equivalent to, and easier to type than, [object[]]).

    • [array] $fileSystemObjects = Get-ChildItem -Recurse -Filter *.txt

That said, in PSv3+ you often need not worry about whether a given variable contains a scalar (single value) or an array, because scalars can implicitly be treated like arrays: you can call .Count even on scalars, and use indexing ([0], [-1]) - see this answer for details.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • okay, and thanks for the pointers about how to handle types and arrays. I like the `Get-Member` commandlet. – Thufir Apr 19 '19 at 23:07
  • 1
    @Thufir; my pleasure. The answer you accepted tells you what specific types `Get-ChildItem` returns, which may be what you _meant_ to ask, but it is not what you _did_ ask. Please consider the perspective of future readers. – mklement0 Apr 20 '19 at 02:51
  • 1
    TL;DR Use `@(...)` to ensure output is always treated as an array! – xjcl Feb 26 '22 at 03:48
3

Get-ChildItem "C:\test" -Recurse will return an array of FileInfo and DirectoryInfo objects inside an array

We can see an a example showing that here

Get-ChildItem "C:\test" -Recurse | %{
    $_.gettype()
}

Returns

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     DirectoryInfo                            System.IO.FileSystemInfo
True     True     DirectoryInfo                            System.IO.FileSystemInfo
True     True     FileInfo                                 System.IO.FileSystemInfo
True     True     DirectoryInfo                            System.IO.FileSystemInfo
ArcSet
  • 6,518
  • 1
  • 20
  • 34
  • Exactly what I needed to know, thanks. This is a standard idiom for getting the type? – Thufir Apr 19 '19 at 23:04
  • 1
    this answer is misleading, because `Get-ChildItem -Recurse` sometimes does not return an array. see mklement0's answer. – orion elenzil Nov 30 '20 at 18:35
  • @orionelenzil Misleading is a strong word. He asked for a answer and I gave him one. My answer works for what he wanted. He even replied below stating thanks but it was "over his head". Sometimes in-depth explanations are not always needed or wanted. – ArcSet Nov 30 '20 at 19:29
  • I dunno. if `c:\test` contains only a single file, the resulting type is `System.IO.FileSystemInfo`. – orion elenzil Nov 30 '20 at 23:55
  • 1
    You may be right that 'misleading' is too strong a word. I'm picking this nit because this particular behavior has tripped me up several times - I write my script against a search that returns many files and all is well, `$x.Length` equals the number of items, etc, but then my script runs somewhere the search returns only a single item and now my code breaks. `$x.Length` is the filesize of the one file returned, etc. – orion elenzil Dec 02 '20 at 00:33
  • @orionelenzil that is a legit concern. Fair enough – ArcSet Dec 02 '20 at 03:16