JDH
JDH

Reputation: 2822

Powershell: All new-objects are not created equal?

I'm working on some short demo scripts for a presentation on powershell and of course one of my own simple demos has me perplexed. The purpose of this demo is to show different ways to create objects in powershell. Here's my code:

$file = ls 'C:\temp' | Where-Object {$_.Extension -eq '.txt'}
$file.FullName
$path = 'C:\temp\jdh.txt'
$newfile = New-Object -TypeName System.IO.FileInfo -ArgumentList $path
$newfile.FullName

$file.GetType()
$newfile.GetType()
$file
$newfile

$file | Get-Content | out-null
$newfile | Get-Content | out-null

Simple right? Create two FileInfo objects via different methods and read them. The output shows the files are identical:

C:\temp\jdh.txt
C:\temp\jdh.txt

IsPublic IsSerial Name                                     BaseType                                                                               
-------- -------- ----                                     --------                                                                               
True     True     FileInfo                                 System.IO.FileSystemInfo                                                               
True     True     FileInfo                                 System.IO.FileSystemInfo                                                               

LastWriteTime : 4/10/2013 3:38:29 PM
Length        : 46046
Name          : jdh.txt


LastWriteTime : 4/10/2013 3:38:29 PM
Length        : 46046
Name          : jdh.txt

However, when Get-Content tries to read the $newfile object, I get the following error:

Get-Content : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the 
input and its properties do not match any of the parameters that take pipeline input.
At line:16 char:12
+ $newfile | Get-Content | out-null
+            ~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (C:\temp\jdh.txt:PSObject) [Get-Content], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.GetContentCommand

So I noticed in the error it says PSObject which confuses me because in my ouput it seems $newfile is a FileInfo object. So the next logical thing I figured to do would be to typecast $newfile into type [System.IO.FileInfo]. That yielded the same error, except now instead of PSObject it shows FileInfo as the type. So how is it that I'm getting that error if $file and $newfile appear to be identical and get-content worked on $file? Is there some subtle difference when you use the new-object command?

Upvotes: 5

Views: 2141

Answers (2)

Andy Arismendi
Andy Arismendi

Reputation: 52659

I traced the binding with the object created using New-Object and the problem is that Get-Content couldn't bind to any properties on the FileInfo info object.

You can check this out with this command:

Trace-Command -psHost -Name ParameterBinding {$newFile | gc}
Trace-Command -psHost -Name ParameterBinding {$file | gc}

So when you look at the trace of the one that binds successfully you'll see that Literal path is bound to the property starting with Microsoft.PowerShell.Core\FileSystem:: which is PsPath. So on closer inspection comparing the object properties when piping to Get-Member you'll see the one that works has these note properties where as the one created with New-Object does not -

  • PSChildName
  • PSDrive
  • PSIsContainer
  • PSParentPath
  • PSPath
  • PSProvider

Taking a look at the Get-Content cmdlet parameters we can see that LiteralPath has an alias of PsPath:

(Get-Command -Name Get-Content).ParameterSets

  Parameter Name: LiteralPath
    ParameterType = System.String[]
    Position = 0
    IsMandatory = True
    IsDynamic = False
    HelpMessage =
    ValueFromPipeline = False
    ValueFromPipelineByPropertyName = True
    ValueFromRemainingArguments = False
    Aliases = {PSPath}
    Attributes =
      System.Management.Automation.AliasAttribute
      System.Management.Automation.ParameterAttribute

If you add a property to the New-Object created FileInfo it will work:

$newFile | Add-Member -MemberType NoteProperty -Name PsPath -Value 'Microsoft.PowerShell.Core\FileSystem::C:\temp\jdh.txt'

Upvotes: 6

Josh
Josh

Reputation: 10614

Remember, New-Object just creates an instance of a PowerShell object...it has not written anything to file. using

Test-Path $path 

shows that the file does not exist yet. So, adding some code to actually create the file and then assign back to it, will yield you not errors.

Test-Path $path

$stream = [System.IO.StreamWriter] $path
1..10 | % {
      $stream.WriteLine($s)
}
$stream.close()

$newfile = Get-Content $path

$file | Get-Content | out-null
$newfile | Get-Content | out-null

We assign back to $newfile because it is a reference. It doesn't get updated automatically if we create the file after the fact.

Upvotes: -1

Related Questions