0

Hi i am not exactly sure if my wording is right but i need a variable which contains current date/time whenever i write data to log ; how can i do that without initializing everytime.Currently everytime i need a update i use these both statements jointly.Is there an other way of doing this?

$DateTime = get-date  | select datetime
Add-Content $LogFile -Value "$DateTime.DateTime: XXXXX"

please do let me know if any questions or clarifications regarding my question.

Darktux
  • 427
  • 6
  • 16
  • 27

5 Answers5

4

This script make the real Dynamic variable in Powershell ( Thanks to Lee Holmes and his Windows PowerShell Cookbook The Complete Guide to Scripting Microsoft's Command Shell, 3rd Edition)

##############################################################################
##
## New-DynamicVariable
##
## From Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################

<#

.SYNOPSIS

Creates a variable that supports scripted actions for its getter and setter

.EXAMPLE

PS > .\New-DynamicVariable GLOBAL:WindowTitle `
     -Getter { $host.UI.RawUI.WindowTitle } `
     -Setter { $host.UI.RawUI.WindowTitle = $args[0] }

PS > $windowTitle
Administrator: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
PS > $windowTitle = "Test"
PS > $windowTitle
Test

#>

param(
    ## The name for the dynamic variable
    [Parameter(Mandatory = $true)]
    $Name,

    ## The scriptblock to invoke when getting the value of the variable
    [Parameter(Mandatory = $true)]
    [ScriptBlock] $Getter,

    ## The scriptblock to invoke when setting the value of the variable
    [ScriptBlock] $Setter
)

Set-StrictMode -Version 3

Add-Type @"
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;

namespace Lee.Holmes
{
    public class DynamicVariable : PSVariable
    {
        public DynamicVariable(
            string name,
            ScriptBlock scriptGetter,
            ScriptBlock scriptSetter)
                : base(name, null, ScopedItemOptions.AllScope)
        {
            getter = scriptGetter;
            setter = scriptSetter;
        }
        private ScriptBlock getter;
        private ScriptBlock setter;

        public override object Value
        {
            get
            {
                if(getter != null)
                {
                    Collection<PSObject> results = getter.Invoke();
                    if(results.Count == 1)
                    {
                        return results[0];
                    }
                    else
                    {
                        PSObject[] returnResults =
                            new PSObject[results.Count];
                        results.CopyTo(returnResults, 0);
                        return returnResults;
                    }
                }
                else { return null; }
            }
            set
            {
                if(setter != null) { setter.Invoke(value); }
            }
        }
    }
}
"@

## If we've already defined the variable, remove it.
if(Test-Path variable:\$name)
{
    Remove-Item variable:\$name -Force
}

## Set the new variable, along with its getter and setter.
$executioncontext.SessionState.PSVariable.Set(
    (New-Object Lee.Holmes.DynamicVariable $name,$getter,$setter))

There's a Set-StrictMode -Version 3 but you can set it as -Version 2 if you can load framework 4.0 in your powershell V2.0 session as explained Here

The use for the OP is:

 New-DynamicVariable -Name GLOBAL:now -Getter { (get-date).datetime }

Here the Lee Holmes's evaluation (where it is clear what is the real flaw) about the method I used in my other answer:

Note
There are innovative solutions on the Internet that use PowerShell's debugging facilities to create a breakpoint that changes a variable's value whenever you attempt to read from it. While unique, this solution causes PowerShell to think that any scripts that rely on the variable are in debugging mode. This, unfortunately, prevents PowerShell from enabling some important performance optimizations in those scripts.
Community
  • 1
  • 1
CB.
  • 58,865
  • 9
  • 159
  • 159
3

Why not use:

Add-Content $LogFile -Value "$((Get-Date).DateTime): XXXXX"

This gets the current datetime every time. Notice that it's inside $( ) which makes powershell run the expression(get the datetime) before inserting it into the string.

Frode F.
  • 52,376
  • 9
  • 98
  • 114
  • Why are people downvoting me today? :S This is a geat answer! :P – Frode F. Jan 23 '13 at 20:29
  • I didn't downvote, but my guess is it's because you solved the _problem_, but didn't answer the _question_, which @CB.'s answer does. – SpellingD Jul 09 '14 at 20:27
  • The question is "Is there an other way of doing this?", which I answered. I didn't answer the title, but the I answered the question in his post. Actually, a subexpression inside a string could be considered a variable, only you specify a longer name(the expression, which can be reused multiple times without any modifications, just like a variable), so you could even say I answered the question in the title. :) No worries though, it solved @Darktux's problem. – Frode F. Jul 09 '14 at 23:01
2

wrap your two commands in function so you will have just one call ?

function add-log{
(param $txt)
$DateTime = get-date  | select -expand datetime
Add-Content $LogFile -Value "$DateTime: $txt"
}
Loïc MICHEL
  • 24,935
  • 9
  • 74
  • 103
1

Besides these other ways (which frankly I would probably use instead - except the breakpoint approach), you can create a custom object with a ScriptProperty that you can provide the implementation for:

$obj = new-object pscustomobject
$obj | Add-Member ScriptProperty Now -Value { Get-Date }
$obj.now
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
1

Using PsBreakPoint:

$act= @'
$global:now = (get-date).datetime
'@
$global:sb = [scriptblock]::Create($act)
$now = Set-PSBreakpoint -Variable now -Mode Read -Action $global:sb

calling $now returns current updated datetime value

One liner:

$now = Set-PSBreakpoint -Variable now -Mode Read -Action { $global:now = (get-date).datetime }
CB.
  • 58,865
  • 9
  • 159
  • 159
  • 1
    @Graimer just put this in your profile and use $now wherever you are, no complication here i think this should be a built in ps var. – Loïc MICHEL Jan 23 '13 at 15:15
  • 1
    For a profile-script, I agree. :) The question was however for a script(i guess), and for that both a function or `$(..)` is better, but that's just my opinion. – Frode F. Jan 23 '13 at 15:19
  • Well except when you're done debugging a script and execute `get-psbreakpoint | remove-psbreakpoint`. At that point $now returns a fixed time value which is worse than an error. – Keith Hill Jan 23 '13 at 17:26
  • Just to add another opinion: If you are aware we have a variable like this, I think it is unwise to run `get-psbreakpoint | remove-psbreakpoint` when it is easily avoidable with a where-object. Maybe too many characters of code? – CB. Jan 23 '13 at 17:44
  • 1
    It is just something that you could easily forget. When I'm debugging a script and setting breakpoints hither and thither, I usually wipe them all out when I'm done. It is an interesting and clever approach to be sure. I just think it is a bit brittle. If I got a script from someone that was setting breakpoints in my session, I'd find that a bit annoying. – Keith Hill Jan 23 '13 at 18:11