6

I essentially want to create a variable that would be executed every time. For the simplest example:

$myvar = `write-host foo`;

Then everytime I referenced $myvar, it would output foo:

dir $myvar

Directory of foo:

The point being that the write-host foo portion would be re-executed everytime I reference $myvar

esac
  • 24,099
  • 38
  • 122
  • 179
  • 1
    Why not use a method? http://www.computerperformance.co.uk/powershell/powershell_functions.htm – asawyer Nov 17 '10 at 20:28
  • In your example, dir $myvar wouldn't work, because write-host wouldn't be output for dir. You want write-output, but you don't really need that, you just need `$myvar = 'foo'` – Jaykul Nov 18 '10 at 20:45

3 Answers3

5

It's doable in managed code (C#/VB) by creating your own PSVariable derived class, but not directly in pure script, sorry. I say "pure script" because in powershell v2 you could inline the C# with add-type. That said, you could hack it in script by relying on implicit ToString calls but this would not be reliable in every situation. Example:

# empty custom object
$o = new-object psobject

# override ToString with a PSScriptMethod member
$o.psobject.members.add((new-object `
     System.Management.Automation.PSScriptMethod "ToString", {
         "ticks: $([datetime]::now.ticks)" }))

ps> $o
ticks: 634256043148813794

ps> $o
ticks: 634256043165574752

Note the tick count is different on each evaluation of the variable. If of course if you just use a regular function instead of a variable, this is much easier.

function Ticks { [datetime]::now.ticks }

# use as a parameter - note the use of ( and )
ps> write-host (ticks)
634256043148813794

# use in a string - note the use of $( and )
ps> write-host "ticks $(ticks)"
ticks 634256043165574752

Hope this helps

-Oisin

x0n
  • 51,312
  • 7
  • 89
  • 111
  • 1
    I love that you thought about using the ToString override for that. If you go that way, make sure that you create the variable ReadOnly, Constant, AllScope so it can't get overwritten, and remember that it's only that when you convert it to string. Otherwise it's something else. x0n: You're a sick, sick genius :) – Jaykul Nov 18 '10 at 20:38
  • It for some reason doesn't work the same as an automatic variable, but really close. For example: "dir c:\;dir $$" will both do the same thing. But with your method above "dir c:\;dir $o" does not. It requires that I do "dir c:\;dir ($o)" which is quote annoying :P – esac Nov 18 '10 at 23:30
  • for the record: $$ is like the $o in my example (that I just added below) it's not calculated on demand, it's set after each command is run. I meant to mention that alternative yesterday, but I got distracted. – Jaykul Nov 19 '10 at 15:02
  • Is there a reason you used $o.psobject.members.add instead of the Add-Member cmdlet? – OldFart Nov 19 '10 at 18:53
  • @oldfart - no, no reason other than showing off some of the inner workings of psobjects. add-member would also work. – x0n Nov 24 '10 at 22:02
  • 1
    @esac - like I said, the first example was only for fun - it only works where powershell treats the variable as a string (which means ToString gets called.) If in the context of a plain object, it won't work. The point to take from this is you should be using a function, not a variable. – x0n Nov 24 '10 at 22:04
  • @jakyul - hah, i may be twisted but you're the one who wrote a powershell host. – x0n Nov 24 '10 at 22:04
3

It’s not that difficult to complete what @x0n mentions. We can embed a piece of C# into our code and get a real automatic variable:

# define a class of my variable, override the Value property
Add-Type @'
using System;
using System.Management.Automation;
public class MyVariable : PSVariable
{
    public MyVariable(string name) : base(name) {}
    public override object Value
    {
        get { return DateTime.Now.Ticks; }
        set { }
    }
}
'@

# install the variable
$ExecutionContext.SessionState.PSVariable.Set((New-Object MyVariable Ticks))

# test
$Ticks
sleep 1
$Ticks
sleep 1
$Ticks

About PSVariable:

http://msdn.microsoft.com/en-us/library/system.management.automation.psvariable

Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
2

The other way you can implement this sort of thing simply is to force recalculation in the prompt function so that the variable is updated every time a command is completed and the prompt is displayed:

$MySecretOldPromptStorage = ${Function:Prompt}
function prompt { $o = $([datetime]::now.ticks); &$MySecretOldPromptStorage }

Of course, that results in it being run every time instead of on-demand

Jaykul
  • 15,370
  • 8
  • 61
  • 70