nohandle

This user hasn't shared any biographical information

Homepage: http://www.jakubjares.com/


Posts by nohandle

New-ChristmasTree

Just a quick post to wish merry Christmas and happy new year to all the readers.

function New-ChristmasTree
{
@'
        .
     __/ \__
     \     /
     /.'o'.\
      .o.'.
     .'.'o'.
    o'.o.'.o.
   .'.o.'.'.o.
  .o.'.o.'.o.'.
     [_____]
      \___/
'@
}
Thanks ldb for the awesome ASCII art.

$() is for subexpression

Using subexpressions in expanding string

Sub expressions can be used in two general situations. The first and the important one is to mark a part of quoted string that should be expanded. If you place expanding “string” in PowerShell script the interpreter checks if there is anything to expand, it looks for variable names and subexpression if any are found they get invoked (expanded). As you can see this script does not use any subexpressions but still the variable ‘win’ gets expanded:

$win = Get-Item  $env:windir
"Path to the Windows directory is $win."
Path to the Windows directory is C:\Windows.

Now let’s try to find out when the directory was created looking at the CreationTime property:

"The folder was created on $win.CreationTime."
The folder was created on C:\Windows.CreationTime

Not the result we wanted, but if you think about it the behavior is reasonable because the variable name could be followed by anything. To explicitly mark the part of the string that should be expanded use the subexpression sign as follows:

"The folder was created on $($win.CreationTime)."
The folder was created on 07/14/2009 04:37:05.

That’s more like it.

In the sub expression you can use more than one command and also more pipelines, as shown in this example:

"The first and the last processes are $($first = Get-Process |
	select -First 1 -ExpandProperty Name ; $last = Get-Process |
		select -Last 1  -ExpandProperty Name; $first, $last -join ", ")."
The first and the last processes are armsvc, wuauclt.

Useful but if you look at it, it makes the code unreadable and the syntax is not highlighted in IDE. It is best to avoid passing long commands to the subexpression; in some cases you can use the formatting operator ‘-f’ instead or process the command before, save its output to a variable and pass the variable to the string.

From my point of view this usage of sub expressions is the only one should’t make you stop and think: “Why am I doing it this way?”

Process more commands in one line

The other case is to pass more that one command as one command, like this for example:

$factorialOfFive = $($fact = 5; 1..$fact | foreach {$result=1}{$result *= $_}{$result})
$factorialOfFive
120

It works but there is no added value over doing it on more lines, the $fact variable is still accessible after the subexpression is run, because no new scope was created. It is just less readable way of doing this:

$fact = 5
$factorialOfFive = 1..$fact | foreach {$result=1}{$result *= $_}{$result}
$factorialOfFive

The best way to do this achieve the same goal is to create a helper function that will take the number as input and will output the factorial, not polluting your session with unnecessary variables.

Quite often people use the subexpression when calling a cmdlet, getting property of an object or even pass string with subexpression that contains value. In most cases the task can be achieved just with parentheses. And if cannot, again, stop and think if it is necessary to do it that way. There are limitations to the commands that can be processed in parentheses that you should be aware of: It cannot contain more commands separated by ‘;’ or more that one pipeline. The following is legal because there is only one pipeline:

1..2 | %{$_} | %{$_} | %{$_}

This is not and you will recieve an error if you try to run it:

($a=1;$b=0)

By now it should be obvious that the following examples produce the same results. Some of them making your fingers tired, some not.
Don’t:

$(Get-Date).Month
$start=$(get-date).addMinutes("$(-10)")
$end = $(Get-Date)
New-TimeSpan -Start $($start) -End $($end)

Do:

(Get-Date).Month
$start=(get-date).addMinutes(-10)
$end = Get-Date
New-TimeSpan -Start $start -End $end

Static methods

Today we’ll look at using static methods in PowerShell. Unlike normal methods a static method can be called without instantiating the class that contains it. This is useful concept for creating classes that hold constant data and provide their methods to others – excellent example is the System.Math class which holds only static functions (In fact, the whole class is static.). Static functions are also used to provide instrumentation to work with instances of class (like a compare function) or to create specialized version of class (like the StartNew method on System.Diagnostics.StopWatch class).
Finding which static methods are available for use is especially easy whe using IDEs like PowerGUI, or PowerShell ISE. You just use a type literal followed by “::” operator and the code completion will show you the methods available.
Listing static methods in PowerGUI

On console you need to use the Get-Member cmdlet with the Static parameter to list static functions, as such:

[DateTime] | Get-Member -Static 
   TypeName: System.DateTime

Name            MemberType Definition
----            ---------- ----------
Compare         Method     static int Compare(System.DateTime t1, System.DateTime t2)
DaysInMonth     Method     static int DaysInMonth(int year, int month)
---- cut ----

To show you an example of static methods usage, I wrote a simple function to test if it is Christmas today. Btw in Czech Republic the Christmas are celebrated on the evening of December 24th, I don’t want to miss my presents, so the function is designed to suite my needs. ;)

function Test-Christmas
{
	$christmas = New-Object -TypeName System.DateTime `
	-ArgumentList ([datetime]::Today).Year,12,24
	$today=[DateTime]::Today

	#returns 0 if equals else returns -1 or 1
	-not [DateTime]::Compare($christmas,$today)
}

Line 3&4: Creating a new object of System.DateTime type using on of its constructors. The constructor accepts four integer values on the output representing year, month and day respectively. Hours, minutes and the rest of the properties on the object are set to zeros.
The Today static method, is used to provide the correct year to the constructor.
Line 5: Static function today is used to retrieve the current date. Only Year, Month and Day properties are filled with data, other properties are zero.
Line 8: Here I use the Compare method that takes two DateTime objects and compares them. It returns 0 if the objects equals or ±1 if not. Because 0 when converted to Boolean type returns false and other numbers return True. The Compare function returns the exact opposite of the desired output. Use the -not operator to negate the boolean value.
Not to saying static methods are unusable in any way.

There is of course an easier way to implement the whole function using just Get-Date cmdlet and a condition.

Testing the function

Test-Christmas
false

The output of the function is false unfortunately. I’ll have to wait for my presents few more days.