views:

165

answers:

2

I would like to retrieve the contents of a file, filter and modify them and write the result back to a file. I do this:

PS C:\code> "test1" >> test.txt
PS C:\code> "test2" >> test.txt
PS C:\code> $testContents = Get-Content test.txt
PS C:\code> $newTestContents = $testContents | Select-Object {"abc -" + $_}
PS C:\code> $newTestContents >> output.txt

output.txt contains

"abc -" + $_                                                                                                           
------------                                                                                                           
abc -test1                                                                                                             
abc -test2

What gives with that first line? It's almost like foreach gives back an IEnumerable - but $newTestContents.GetType() reveals it is an object array. So what gives? How can I get the array to output normally without the strange header.

Also bonus points if you can tell me why $newTestContents[0].ToString() is a blank string

+2  A: 

Use ForEach instead of the Select-Object

Nestor
how exactly would I use foreach to create a transform?
George Mauer
George,.. use your same code.. but replace Select-Object by ForEach. It should work as without any other modification.
Nestor
Ahh, ok thanks. I was thinking from the C# LINQ point of view where ForEach is a void return
George Mauer
+3  A: 

Also bonus points if you can tell me why $newTestContents[0].ToString() is a blank string

If you look at its type, it is a PSCustomObject e.g.

PS> $newTestContents[0].GetType().FullName
System.Management.Automation.PSCustomObject

If you look at PSCustomObject's ToString() impl in Reflector you see this:

public override string ToString()
{
    return "";
}

Why it does this, I don't know. However, it is probably better to use string type coercion in PowerShell e.g.:

PS> [string]$newTestContents[0]
@{"abc -" + $_=abc -test1}

Perhaps you were looking for this result though:

PS> $newTestContents | %{$_.{"abc -" + $_}}
abc -test1
abc -test2

This demonstrates that when you use Select-Object with a simple scriptblock the contents of that scriptblock form the new property name on the PSCustomObject that is created. In general, Nestor's approach is the way to go but in the future if you need to synthensize properties like this then use a hashtable like so:

PS> $newTestContents = $testContents | Select @{n='MyName';e={"abc -" + $_}}
PS> $newTestContents

MyName
------
abc -test1
abc -test2


PS> $newTestContents[0].MyName
abc -test1
Keith Hill