Tied Variables in PowerShell

April 12, 2011

5 comments

When PowerShell starts it creates a bunch of Automatic Variables. One of them is $PWD. $PWD contains a path object that represents the full path of the current directory (provider sensitive). Typically, when you assign a value to a variable, the value is evaluated once and from then on it’s “static”, each time you call the variable the returned value is always the same. The beauty of $PWD is that its value is calculated each time you call it.

PS> $PWD

Path
—-
D:\scripts

PS > Set-Location HKLM:\SOFTWARE
PS > $PWD

Path
—-
HKLM:\SOFTWARE

 

$PWD contains a rich .NET object (System.Management.Automation.PathInfo), not just a string path, so we can also interact directly with its members:

PS > $PWD.Provider.Name
Registry

 

I was trying to create a variable similar to $PWD ($Now) to get the current date and time, but no matter what I tried, the value of $Now always returned the same date and time. The reason behind creating $Now was this. Let’s say you want to get the DayOfWeek value of a DateTime object, what we often do is:

## two lines of code
PS > $date  = Get-Date
PS > $date.DayOfWeek

Or

## using a .NET class
PS> [DateTime]::Now.DayOfWeek

Or

## enclosing in parenthesis
PS> (Get-Date).DayOfWeek

 

I wanted is to use the third example but avoid enclosing Get-Date it in parenthesis. One of the ways I was trying is with a function:

PS > function Now {Get-Date}

It works, I can now get a new fresh DateTime object each time I call the function, but I can’t access the result object members without enclosing it in parenthesis:

PS> (Now).DayOfWeek

 

Ultimately, this is how I wanted it to be:

$Now.DayOfWeek

Simple to use and very short to type. Another thought was to create a new Alias but the result was the same as using a function, parenthesis were required to access object members. So I posted a question to the PowerShell MVP private list and soon enough Lee Holmes, pointed me to this blog post. Here’s the code I used to create $Now:

Add-Type -TypeDefinition @”
using System;
using System.Management.Automation;
public class NowVariable : PSVariable
{
public NowVariable ()
  : base(“Now”, 0, ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope)
  {}
       public override object Value
        {
            get
            {
               return DateTime.Now;
            }
        }
}
“@

$ExecutionContext.SessionState.PSVariable.Set((New-Object -TypeName NowVariable))

 

Paste this code into your console and there you have it! A variable that evaluates its value each time you call it.

PS>$Now
Tuesday, April 12, 2011 10:48:41 AM

PS>$Now
Tuesday, April 12, 2011 10:48:43 AM

PS>$Now
Tuesday, April 12, 2011 10:48:45 AM

PS> $Now.DayOfWeek
Tuesday

 

Now we can access any member of the result DateTime object without parenthesis. Thanks Lee! The next step is to ask the PowerShell team to include $Now in the next version of PowerShell and the way to do that is to file a suggestion on the Microsoft Windows PowerShell Customer Connection website. The PowerShell team review the items suggested by the community and if it has enough votes it will find its (hopefully) way to the next version of PowerShell.

Do you want to have $Now as a new automatic variable in PowerShell 3.0? Vote it up HERE.

 

UPDATE: Check out this awesome post, by Robert Robelo, where he shows a how to get the same functionality in pure script and it also works in PowerShell v1!

 















Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

5 comments

  1. cmcknz77May 5, 2011 ב 02:35

    I like it…
    I was thinking that I’d be able to do something similar to create an automatic variable for todays powershell.com tweettip:

    [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]'{DCB00C01-570F-4A9B-8D69-199FDBA5723B}’)).IsConnectedToInternet

    So that in my scripts I could just say:
    if($connectedtointernet){do {something cool}}

    But I can’t get it to work. I reckon I’m crossing language boundaries here right?

    Reply
  2. ScriptFanaticMay 5, 2011 ב 18:03

    Yup, the code must be written in C#.

    Reply
  3. Martin77September 2, 2011 ב 21:58

    A shorter way, would be to use a breakpoint when the $Now variable is being read, and set the variable’s new value using the action parameter.
    it adds up to:

    $global:Now = Set-PSBreakpoint -Variable Now -Mode Read -Action { Set-Variable Now (Get-Date) -Option ReadOnly, AllScope -Scope Global -Force }

    Reply
  4. Maridsolar1985February 21, 2013 ב 08:53

    It is a pity, that now I can not express – it is compelled to leave. But I will return – I will necessarily write that I think on this question.

    Reply