Archive for the “Scripting” Category

Holy Schnikees there are a lot of operators in Powershell! This may not be an exact count  but looking through the help file it looks like there are 57 operators!  I am using the CTP – Powershell V2 -  which introduced a handful of new operators, particularly  -join and -split.

I’d like to start a series on some of the operators and how they can be used. I’d like to start with the most basic of operators, the -eq operator. This is the equality operator.

Note that this is different than the assignment operator “=” which is used to assign values to variables.

In a lot of programming languages, the “==” operator is used for equality.  Here’s a quick example.

   1: PS C:\Users\andys> 1 -eq 1

   2: True

   3: PS C:\Users\andys> 2 -eq 3

   4: False

This is pretty normal, (and boring). Do notice that this returns the boolean value True.

But what about if we compare an array  to an integer.

   1: PS C:\Users\andys> function new-array {,$args}

   2: PS C:\Users\andys> $i = new-array 1 2 3 4 5

   3: PS C:\Users\andys> $i

   4: 1

   5: 2

   6: 3

   7: 4

   8: 5

   9: PS C:\Users\andys> $i -eq 3

  10: 3

Interesting that the value that is returned is the value of the item in the array that was a match. It was NOT a boolean. You can read about my new-array function here for more info, but it allows you to create an array with a lot less typing.

The same is true with strings.

   1: PS C:\Users\andys> $s = new-array one two cat dog

   2: PS C:\Users\andys> $s -eq "cat"

   3: cat

   4: PS C:\Users\andys> $s -eq "not-here"

   5: PS C:\Users\andys>

However, if we use this in an if statement, it will get converted to a boolean value.

   1: PS C:\Users\andys> if ($s -eq "cat") {return $true} else {return $false}

   2: True

   3: PS C:\Users\andys> if ($s -eq "no") {return $true} else {return $false}

   4: False

   5: PS C:\Users\andys>

We can use an implicit cast to show this:

   1: PS C:\Users\andys> [bool]($s -eq "cat")

   2: True

   3: PS C:\Users\andys> [bool]($s -eq "not here")

   4: False

   5: PS C:\Users\andys>

I am continuously amazed at how deep you can go with exploring Powershell and looking at subtle nuances such as these. It’s really pretty awesome to think about how all these operators just work as you expect, given the context you are using them in.

 

Andy

Comments No Comments »

The beauty of Powershell is that everything in Powershell is an object. You take objects, pass them to objects, and get objects back. One of the other great features of Powershell is compatibility with older CMD commands. The problems arises though when we want to get the two working together.

A big issue with regular commands is the each one has its own way of displaying output. Interpreting and using this output programmatically can be tricky at best. Mostly for the sake of an example, I’d like to walk through the process of objectifying the output of ipconfig.

To begin, we will need to run ipconfig and store the output in a variable. However, when we run it, lets filter down as much as possible with some broad parsing and then we can filter it down and fine tune our parsing as we get closer to the actual information we want.

So here we go:

   1: PS C:\> $ip = ipconfig | Select-String "IPv4|Subnet|Gateway" | select-object -first 3
   2: PS C:\> $ip
   3:  
   4:    IPv4 Address. . . . . . . . . . . : 192.168.15.220
   5:    Subnet Mask . . . . . . . . . . . : 255.255.255.0
   6:    Default Gateway . . . . . . . . . : 192.168.15.1
   7:  
   8:  
   9: PS C:\>

Using select-string we can get all the lines that have “IPv4 OR Subnet OR Gateway.” Next we pipe this to select-object and select the first 3 lines that match our criteria. This way we are only getting the first adapter.

Now that we have this info store in the variable $ip we can start looking at parsing out the different lines. Lets just look at working with the first line as a demonstration.

   1: PS C:\> $ip[0]
   2:  
   3:    IPv4 Address. . . . . . . . . . . : 192.168.15.220
   4:  
   5: # Use multi variable assignment to get the output of the split on the string
   6: # Note that we split on ":" so we IPv4 Address set to $prop and the IP Address 
   7: # gets set to $value
   8:  
   9: PS C:\> $prop,$value = $ip[0].ToString().Split(":")
  10: PS C:\> $prop
  11:    IPv4 Address. . . . . . . . . . .
  12: PS C:\> $value
  13:  192.168.15.220
  14:  
  15: PS C:\> # Now lets clean it up a bit
  16:  
  17: PS C:\> $prop.Trim(" :")
  18: IPv4 Address. . . . . . . . . . .
  19:  
  20: # Trim off the spaces and periods.
  21: PS C:\> $prop.Trim(". :")
  22: IPv4 Address
  23:  
  24: # for this we can just trim off the leading space
  25:  
  26: PS C:\> $value
  27:  192.168.15.220
  28: PS C:\> $value.Trim()
  29: 192.168.15.220

We use the split method to extract the property and value, essentially cutting each line in half at the “:”

Once we get the individual property and value entries we can clean them up with a little haircut… using the Trim method.

The last thing we will need to do is add these properties and their values to an object. This is accomplished using the add-member cmdlet.

   1: PS C:\> $prop = $prop.Trim(". :")
   2: PS C:\> $prop
   3: IPv4 Address
   4: PS C:\> $value = $value.Trim()
   5: PS C:\> $value
   6: 192.168.15.220
   7: PS C:\> $ipo = New-Object psobject
   8: PS C:\> Add-Member -InputObject $ipo noteproperty -Name $prop -Value $value
   9: PS C:\> $ipo
  10:  
  11: IPv4 Address
  12: ------------
  13: 192.168.15.220
  14:  
  15:  
  16: PS C:\>

Now if we wrap all this up into a foreach-object command.

   1: PS C:\> $ip = ipconfig | Select-String "IPv4|Subnet|Gateway" | select-object -first 3
   2: PS C:\> $ip | % {$ipo = New-Object psobject} {$prop,$value = $_.ToString().Split(":");
   3: >> Add-member -InputObject $ipo noteproperty -Name $prop.Trim(". :") -Value $value.Trim()}
   4: >>
   5: PS C:\> $ipo
   6:  
   7: IPv4 Address                                              Subnet Mask                                               Default Gateway
   8: ------------                                              -----------                                               ---------------
   9: 192.168.15.220                                            255.255.255.0                                             192.168.15.1
  10:  
  11:  
  12: PS C:\> $ipo | fl *
  13:  
  14:  
  15: IPv4 Address    : 192.168.15.220
  16: Subnet Mask     : 255.255.255.0
  17: Default Gateway : 192.168.15.1
  18:  
  19:  
  20:  
  21: PS C:\>

Really the only tricky thing here is that we changed $ip[0] to $_ so we could process the current object each time using the “%” which is the alias for the ForEach-Object cmdlet..

This is all sweet and nice but still its a lot of work for one command and doing all the string manipulation is  a bit tough, and not repeatable for other commands. So this brings up a question for which I do not have an answer. in the long run,  do we adopt commands and coerce them into the world of objects or do we build functions/Cmdlets to replace them.

I am sure the answer is the same as what my grandpa said when I asked him if he wears briefs of boxers… “Welll.. Depends”

What say you ?

Andy

Comments 2 Comments »

Powershell can slice and dice arrays all day long, and we use them all the time. However, when we are testing stuff and creating dummy arrays the syntax can be a bit awkward. Its quite straightforward and understandable, but its just a pain to type

For example

   1: PS 39 >  $i = "one","two","three"
   2: PS 40 >  $i
   3: one
   4: two
   5: three

Now for me that is just way too many quotes and commas. This can easily be addressed with a new function called New-Array.  Are we going to have to parse and add quotes and do all kinds of crazy stuff to get this working ? Not really. Here’s the function and how to use it.

   1: PS 41 >  function new-array {$args}
   2: PS 42 >  $i = new-array one two three four five six
   3: PS 43 >  $i
   4: one
   5: two
   6: three
   7: four
   8: five
   9: six

Ah, now that is much better. All this does is return the $args array which is an array that you get in every function be default. $args[0] is the first argument. $args[1] is the second etc etc.

That being said we are not quite there.  Lets say you want to be really sure you create an Array. In the case where you originally have only 1 $arg it will return a scalar. Let me demonstrate.

   1: PS 58 >  function new-array {$args}
   2: PS 59 >  $i = new-array 1
   3: PS 60 >  $i += 2
   4: PS 61 >  $i
   5: 3

 

In this case, $i ends up being a scalar with a value of 1 and then when you add 2 to it, you get $i = 3.

What we want is to be able to use the += operator to add a value to the array. There are multiple ways to force a scalar to be an array when you declare it but i think the comma operator is the shortest.  So we make one quick addition to our new-array function.

   1: PS 62 >  function new-array {,$args}
   2: PS 63 >  $i = new-array 1
   3: PS 64 >  $i +=2
   4: PS 65 >  $i
   5: 1
   6: 2

Pretty cool!

Comments 3 Comments »

An extremely powerful feature (that isn’t all that well known) is the ability to assign values to multiple variables in one line. Take the following example:

PS> $one,$two,$three = "first","second","third"
PS> $one;$two;$three
first
second
third

This technique can be used in custom functions to parse the $args value

PS> function show-greeting {$greeting,$name = $args; "Hello $name, $greeting"}
PS> show-greeting "Nice to meet you" "Andy"
Hello Andy, Nice to meet you

One last thing to note is what happens if you have more values than variables. Does it blow up. An error. Not really. Check this out

PS C:\Users\andys> $one,$two = "first","second","third",4
PS C:\Users\andys> $one
first
PS C:\Users\andys> $two
second
third
4
PS C:\Users\andys> $one.count
PS C:\Users\andys> $two.count
3
PS C:\Users\andys>

The last variable gets the remaining values assigned to it.

Comments 3 Comments »

In C# you can build an Enum that essentially makes your code much easier to read.

 

This is especially useful for bit flags where you want to represent something that is quite readable but in the background there could be all kinds ugly data that is not easy to remember.

We can do something similar in PowerShell using a hash table. For example, lets say we have a set of temperatures that we want to refer to throughout a script.

$temps = @{}

$temps.freezing = 32

$temps.boilng = 212

$temps.absoluteZero = −459.67

$temps.comfortable = 72

 

From here you you could take a variable $temp and say something like

if ($temp -lt $temps.freezing) {"Watch for Snow!"}

if ($temp -lt $temps.absoluteZero) {"You broke physics"}

 

This just may be a way to make scripts a bit more readable and easier to update after the fact.

 

Andy

Comments No Comments »

Scott Hanselman recently posted The Weekly Source 13 – Fibonacci Edition. . It was a very interesting read, but it was missing the Powershell version.

As in the Ruby example, you can use multiple assignments in Powershell as well

Function Get-Fib ($n) {
     $current = $previous = 1;
     while ($current -lt $n) {
           $current;
           $current,$previous = ($current + $previous),$current}
     }
Get-Fib 100

Bruce’s book, Powershell in Action, uses a slightly more terse version of this function in his discussion of multiple variable assignment.

Comments 2 Comments »

Quite often, for one reason or another, I need to create a list of IP Addresses. The Powershell Range Operator “..” comes in handy for such a situation. To get a list of all the IP’s in 10.10.10.0/24 I can run the following command

 

[sourcecode language='css'] $ips = 1..254 | % {“10.10.10.$_”} [/sourcecode]

 

Now I have an array of my IP’s that I can work with

Comments No Comments »

One of the great things about Powershell is that you can use all your standard command line tools, such as ipconfig and net.exe. The only issue is that these tools output text and Powershell works with objects.

The output of commands such as ipconfig is actually an array.

PS 24 >  (ipconfig).GetType().BaseType.FullName
System.Array

Each item in that array is a string

PS 31 >  (ipconfig)[4].gettype().FullName
System.String

So really you end up with an array of strings and if you know the indexes of that array, you can work with much more specific data. For example:

PS 58 >  (ipconfig)[7]
   IPv4 Address. . . . . . . . . . . : 10.2.4.48

But how do you know that this is line 7, or the 7th item in the array. You can run the command and count lines and guess, or you can do something like this:

PS 57 > $i = 0;ipconfig| % {$i++;"$($i-1) `t $_"}
0
1 Windows IP Configuration
2
3
4 Ethernet adapter Local Area Connection:
5
6 Connection-specific DNS Suffix . : corp.example.com
7 IPv4 Address. . . . . . . . . . . : 10.2.4.48
8 Subnet Mask . . . . . . . . . . . : 255.255.254.0
9 Default Gateway . . . . . . . . . : 10.2.4.1
10
11 Tunnel adapter Local Area Connection* 6:
12
13 Media State . . . . . . . . . . . : Media disconnected
14 Connection-specific DNS Suffix . :
15
16 Tunnel adapter Local Area Connection* 7:
17
18 Media State . . . . . . . . . . . : Media disconnected
19 Connection-specific DNS Suffix . : corp.example.com
Now you can know for sure what line you need to access and work with.

(ipconfig)[19] will give me my DNS name.

The other way around this is working with regular expressions to parse the file, but this will get you something really quickly. It works for a bunch of commands as well.

If you are really up for "objectifying" text output, check out Lee Holme's Awk with a Vengeance

Andy

Comments No Comments »

Recently I have been playing with Ruby and I have to say the language is a ton of fun. This one line of code pretty much sold me on the language.
 
5.times {puts "Hello World"}

Similar to Powershell, in Ruby, everything is an object, including primitive times. In Powershell, I could definitely get into some XML goo and spot-weld a method to System.Int32 called times.

What would be really cool is if in Powershell I could expand the int32 class on the fly and not have to mess with XML, just as I can in Ruby.


mystring = "Hello There"
puts mystring.length
class String
 def myownmethod
  puts "Methods on the fly are pretty cool"
 end
end

puts mystring.myownmethod

Below is the output from the code

RubyMate r6075 running Ruby r1.8.6 (/usr/bin/ruby)
messwithruby.rb

11
Methods on the fly are pretty cool
nil

I love Powershell and am a die-hard fan. When it comes to the debate of Powershell vs Ruby I personally take Scott Hanselman’s suggestion. Learn them both and you wil be a better scripter/developer/person. Sure, you can also use add-member for instances of objects, but to be able to create and modify classes in Powershell V2 would be pretty darn cool.

Comments 6 Comments »