CodexArcanum
CodexArcanum

Reputation: 4016

Variable expansion on $_ in a foreach-object?

I have a script with a foreach loop over an array, and it should be using a string that has the current object in it. But the string isn't expanding properly.

$Patterns = ("An", "array", "of", "strings")
$Template = "The current item is $_"

$Patterns | foreach { Does-Something $Template }

The output of does-something just puts the template string up to the variable, but doesn't insert it. This works if I use a foreach statement and an explicit variable name. What do I need to change to make this work?

EDIT:

Ok, I had a mistake in understanding, and left out something important, so let me post the actual code I'm using:

$OutFile = "C:\Searched.txt"
$Path = "C:\Dev\Folder\To\Search"
$Patterns = ( "Big", "Array", "of", "things", "to", "search" )

$exp = @{Expression={$_.FileName};Label="File Name"}, `
       @{Expression={$_.LineNumber};Label="Line #"}, `
       @{Expression={$_.Path.Substring($Path.Length)};Label="Path ($Path)"}, `
       @{Expression={$_.Line.Trim()};Label="Line ($_)"}

$Patterns | foreach { 
get-childitem ($Path + "*") -include *.cs -recurse `
| select-string -pattern ($_ + "TableAdapter") `
| format-table $exp -AutoSize }

So the issue is that I foolishly have $_ embedded into the expression hashtable, which is being passing into format-table, but format-table needs the $_ expression itself. So I'm not actually able to access the outer scope with the string I need.

Now, is there a way to get around that?

Upvotes: 0

Views: 5524

Answers (3)

Mike Shepard
Mike Shepard

Reputation: 18176

Try

$Patterns | foreach {Does-Something $executioncontext.InvokeCommand.ExpandString($Template)}

This should do the string expansion for you.

Upvotes: 2

jpmc26
jpmc26

Reputation: 29944

It seems like what you really need is a function evaluated at the time the loop is run. Maybe you can make $exp a scriptblock. A simple example of what I mean:

PS C:\> $func = [scriptblock]{ 'prefix' + $_ }
PS C:\> @('x', 'y', 'z') | Foreach-object { Write-host (& $func) }
prefixx
prefixy
prefixz
PS C:\>

Or just define a function:

PS C:\> function func() { 'prefix' + $_ }
PS C:\> @('x', 'y', 'z') | Foreach-object { Write-host (func) }
prefixx
prefixy
prefixz
PS C:\>

By referencing $_ in a scriptblock or function, it will be evaluated when called and not before.

Or if you prefer, you may define the scriptblock/function to take the input object as a parameter:

PS C:\> function func($x) { 'prefix' + $x }
PS C:\> @('x', 'y', 'z') | Foreach-object { Write-host (func $_) }
prefixx
prefixy
prefixz
PS C:\>

Upvotes: 0

stej
stej

Reputation: 29479

I don't see that you use $_ from outer scope. But let's assume that you would like to have last column with name "Line for pattern $_", so you would use outer scope from foreach, than the simplest solution would be just change the label in each iteration:

...

$Patterns | foreach { 
  $exp[-1].Label = "Line for pattern $_"     # <<- this one

  get-childitem ($Path + "*") -include *.cs -recurse `
    | select-string -pattern ($_ + "TableAdapter") `
    | format-table $exp -AutoSize 
  }

Upvotes: 0

Related Questions