Get Started with Powershell: Loops and branches
I think I mentioned this before, but I’ll say it again now, the purpose of these little “Get Started” posts is not to teach you Powershell but instead is meant to give you the foundations for moving forward with your own scripting. There are far better tutorials out there and I encourage you to seek them out if that’s what you need. Here I just want to get you started. From the past couple of days we’ve learned 4 key cmdlet’s and methods to help us discover Powershell by asking Powershell. With this post I want to talk about the most common method of creating a loop and branching your code given a particular condition.
Let’s Loop Together
In Powershell, the most common way to do a loop will be to use the ForEach-Object cmdlet. There are two alias’ for ForEach-Object and the second one still makes my head hurt at times: % and ForEach. An alias is something you can setup that allows you to use a command using a different set of characters. GCI is short for Get-ChildItem, % is short for ForEach-Object, etc. Anyway the problem with the ForEach alias is there is also a statement called ForEach–and Powershell can tell which you’re using!
The key to ForEach is that you have an variable of type Array and need to go through each element in the array. I like to think of array’s as spreadsheets of data. A simple array of strings is a spreadsheet with many rows but only one column. But you can have other types of objects collected in an array. Numbers, datetime, etc. But most importantly you can have array’s of objects. Remember how we had loaded a variable with a single object in the last post? What if we loaded that variable with ALL of the services?
$b = Get-Services $b.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array
It’s an Object variable, but the basetype is now System.Array. You’ve now loaded the $b variable with every service on your computer. Can you imagine what would happen if you issued a $b.Stop() method?! It’d kill every service on your computer.
PS U:\> $b.stop() Method invocation failed because [System.Object[]] doesn't contain a method named 'stop'. At line:1 char:8 + $b.stop <<<< () + CategoryInfo : InvalidOperation: (stop:String) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound
Luckily, in this case, Powershell knows $b is a System.Array and doesn’t possess the .Stop() method (though if you pipe $b into Get-Member it will show it to you) and you haven’t just crashed your computer. The trick is we want to look through all of the services and get particular information that we want from them. I’m not going to continue the Stop() method example because once you get it working, it cause havoc on your machine!!
So ForEach-Object (or %) is a pipeline cmdlet, meaning you have to pipe an array into it for it to do anything. You then surround what you want to do with each item in the array with braces (curly brackets), and the nice thing here is you can have an unlimited number of commands in the braces. I plan on going into more detail about pipelining tomorrow so for now let’s just say $_ is a specialized variable that simply refers to the current object in the pipeline (this will make more sense in a second).
$b | ForEach { $_.Name }
This simple little command will loop through each object (row) in $b variable and print on screen the “Name” property of those objects (column). Want to see the status instead?
$b | ForEach { $_.Status }
You can think of ForEach as looping through every row in a spreadsheet, and the property name as going through every column.
So what’s the difference between the ForEach-Object cmdlet and the ForEach statement? The ForEach statement let’s you take a little more control of your code, and make it look a little prettier. Also, if you have a need to run nested loops (loops within loops) then using the ForEach statement allows this by letting you assign a variable name to each instance of the array (row). You don’t use pipeline with the ForEach statement:
ForEach ($Service in $b) { $Service.Status }
As you can see, a little different syntax, but you still have your braces and you still have any code you want in between, but the code definitely looks and reads a little better–especially if you’re coming from a vbScript background. This is how you tell the difference if Powershell is using the cmdlet or the statement: ($Service in $b). What this is saying, I’m going to assign the first element (row) in our array to $Service and run everything in the braces. Then I’m going to assign the next element in the array to $Service and run it again and loop through everything until I’ve run through every element in the array.
If Only That Were $True
If you’ve ever written any kind of code, ever, then you know about the IF statement. I’ll keep it brief here as the IF statement is pretty easy to follow. Here’s the general format:
If ($var -eq "something") { ...do something... } Else { ...do something else... }
Powershell also supports ElseIf, if you’re a fan of it–see what I did there? The difficult part for scripters in Powershell is the logical operators. Powershell has it’s own way of doing things and it’s just something we have to live with. No more If This=That then, you have to use the new way: If (This -eq That) { }. I know it looks pretty wonky but that’s the life we must lead!
To get more information, read up on the help:
Get-Help About_Logical_Operators
Tomorrow we’re going to talk about the Pipeline!
[…] I really want to talk about today is ForEach. As I’ve mentioned in my “Getting Started” series, there are really two versions of ForEach in Powershell, the ForEach-Object cmdlet […]
[…] do you start using Powershell? Powershell Discovery: Variables Get Started with Powershell: Loops and branches Powershell and the Pipeline Powershell Best Practices – Surly […]