0

I'm finding that I am repeating myself in powershell scripts in some cases where execution context matters. Delayed string expansion is one such case:

$MyString = 'The $animal says $sound.'

function MakeNoise{ 
   param($animal, $sound)
   $ExecutionContext.InvokeCommand.ExpandString($MyString)
}

PS> MakeNoise pig oink
The pig says oink.

That long ExpandString() line gets repeated frequently. I'd prefer that line to be terse like this:

xs($MyString)
xs $MyString
$MyString | xs

Any of these would be preferable. My usual strategies of encapsulation in commandlets don't seem to work for this case because the context of the call to ExpandString() is critical.

So my questions are:

  1. Is there a way to create an alias for an object method?
  2. Is there some other way call an object's method in a terse manner while preserving the context of the call?
alx9r
  • 3,675
  • 4
  • 26
  • 55
  • Why not create a function and call it whatever you want? – CB. Feb 19 '15 at 20:40
  • @CB. Because wrapping ExpandString() in a function changes its context rendering the variables to be expanded inaccessible to ExpandString(). – alx9r Feb 20 '15 at 03:38

3 Answers3

1

It seems you need a way to delay evaluating $ExecutionContext until it's time to actually do the string expansion. Here's one way to do that, implemented as a function:

function xs
{
    [CmdletBinding()]
    param
    (
        [parameter(ValueFromPipeline=$true,
                   Position=0,
                   Mandatory=$true)]
        [string]
        $string
    )
    process
    {
        $code = "`$ExecutionContext.InvokeCommand.ExpandString(`"$string`")"
        [scriptblock]::create($code) 
    }
}

Then:

&($MyString | xs)
&(xs $MyString)

The scriptblock is created at runtime, so $ExecutionContext is evaluated for each invocation.

alx9r
  • 3,675
  • 4
  • 26
  • 55
mjolinor
  • 66,130
  • 7
  • 114
  • 135
  • This doesn't seem to work if `xs` is implemented in a separate module from `$Mystring | xs`. – alx9r Feb 20 '15 at 18:35
  • That sounds like a scope issue. It can't fix that. – mjolinor Feb 20 '15 at 18:37
  • I got it working with some minor edits. I've updated your answer (the update is waiting for peer review). Your idea to delay invocation by creating a script block for each string definitely works. – alx9r Feb 20 '15 at 19:40
0

You could just make your own function for this

function xs{
    param(
    [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
    [string]$expand)
    $ExecutionContext.InvokeCommand.ExpandString($expand)
}

function MakeNoise{ 
   param($animal, $sound)
   $MyString = 'The $animal says $sound.'
   xs $MyString
   # $MyString | xs     <--  would also work
}

MakeNoise pig oink
Matt
  • 45,022
  • 8
  • 78
  • 119
  • The problem with this is that the `xs` function is only useful if it is in the same context (e.g. module) as `MakeNoise`. Specifically, if you put `xs` in another module, say, `utility.psm1` and put `MakeNoise` in `MakeNoise.ps1` the result is `The says .` because `xs` is in a context where `$animal` and `$sound` don't exist. – alx9r Feb 19 '15 at 22:09
  • You would have that issue for any function or aliases. You could define it as global `function global:xs`. Never played with that much so I'm not sure it will work. – Matt Feb 19 '15 at 22:20
  • Hmm...if there existed a Cmdlet called say `Expand-String` that did the same thing as `ExpandString()`, then you could set an alias `xs` for it and it would work the same. An using an alias to call a funtion doesn't change the context for a function in the same way as wrapping a function in another function does. – alx9r Feb 19 '15 at 22:38
  • If that was a question the answer is yes. Aliases do not change the functionality of functions/cmdlets. Just decrease typing and brevity is some cases. – Matt Feb 20 '15 at 01:40
-1

Look at the cmdlet New-Alias - https://technet.microsoft.com/en-us/library/hh849959.aspx

Sean Rhone
  • 250
  • 2
  • 7
  • `New-Alias` seems to create aliases only for a "cmdlet, function, script file, or operable program". Do you have an example of how `New-Alias` can be used to create an alias for an object method? – alx9r Feb 19 '15 at 20:46