Categories
Development Powershell Tutorials

PowerShell – Stuck in the loop

Having to perform the same repetitive task over and over again is not fun and only dulls the brain. But when it has to be done, PowerShell doesn’t mind and is ready to help.

Like in many programming languages Powershell provides us with a few different loops.

ForEach-Object

Might be the most straight forward loop and one you’ll use often.
This loop is used in one liners and takes pipeline input.

ForEach-Object can be shortened down to ForEach or just simply a percent sign, %.

Examples

If you are following along, open at least one Notepad before proceeding so that you have something to collect. If you have a Notepad already opened with unsaved text in it, save it before trying these since we will be closing ALL Notepad processes.

# EXAMPLE ONE #################################################

# Foreach-Object

$notepads = Get-Process notepad
$notepads | ForEach-Object{ Stop-Process $_ }

# EXAMPLE TWO #################################################

# Foreach-Object - Multiple commands and source directly from a command

Get-Process notepad | ForEach-Object{ Write-Host "Stopping process $($_.name) with process ID: $($_.Id)" ; Stop-Process $_ }


# EXAMPLE Three ###############################################
Get-Process notepad | % { Stop-Process $_ }

In the first example we load a variable with all Notepad processes.
Next we pipe that variable to ForEach-Object and within the script block (the curly brackets) is the code we want to run for each Notepad process.
In this case we are running Stop-Process.
$_ represents the current object in the pipeline and the current item (Notepad process) being looped.

In example two we skip the initial $notepads variable and instead directly pipe the input to ForEach-Object.
This time we’re going to run two commands for each Notepad process and we will do so by using the semicolon ( ; ).
This keeps it a one liner but still allows us to use several commands like if we had several lines.
Using Write-Host we output some information to console about the processes we are stopping.

$_ has several attributes with information we might be interested in, like Name and Id which we can access by typing $_. and the attribute name (Name/Id).
Since it is within a string the .Name or .Id would be considered text, so we use a dollar sign and two parentheses, $(), to make it a temporary variable.

The third example is just to showcase using the percent sign alias for ForEach-Object.

ForEach

Sibling to ForEach-Object, ForEach is more used for scripts. Noticeable change is that ForEach does not use $_ but instead assigns a named variable.
This is my favorite loop.

Examples


# Simple Foreach loop #######################################

$notepads = Get-Process notepad

foreach($notepad in $notepads){
    Stop-Process $notepad
}


# More advanced Foreach loop ################################

$notepads = Get-Process notepad
$count = $notepads.count
$i = 1

foreach($notepad in $notepads){

    # These variables will be overwritten each loop
    $processID = $notepad.id 
    $name = $notepad.Name

    Write-Host "[$i/$count] Stopping process $name with process ID: $processID"

    Try{
        # Using "-ErrorAction Stop" makes every error a terminating error which will trigger our try catch. 
        Stop-Process $notepad -ErrorAction Stop
    }
    Catch{
        # Write a warning when we receive an error trying to stop the process. This will log some info we decide (name and pid) and then the error message returned by Powershell. 
        Write-Warning "Error when stopping process $name with process ID: $processID. Error message was: $($_.Exception.Message)"
    }

    # Increase $i by one
    $i++
}

Simple foreach loop starts with saving the Notepad processes to a variable, but this time we’re not using the pipe to pass the values to the loop.

So the code goes like “for each $notepad in $notepads”.
$notepads is our variable we created and added the process data into.
$notepad is a new variable that the foreach loop will assign a new value to each time it loops, similar to $_.
in” is just a keyword used here, gives a nice flow to it.
The $notepad variable doesn’t have to be named like that, you could name it $itSureIsNiceLearningPowerShell or whatever you like 🙂

For this short example it doesn’t really make sense and you’d be better of using ForEach-Object, but lets move on the next example.

In the more advanced example we get a better taste of using the foreach loop but we start with some variables.
We get a variable containing the count of notepads, so we know how many Notepad processes we have caught.
Next up with create the $i variable and set it to 1, this will be used to tell us which Notepad process we are currently processing (like 2/7, 3/7, 4/7 and so on).

Inside the loop we create two more variables, these variables will be overwritten every time it loops.

The information we write to the console is the process name and Id, but also [$i/$count] this is useful when looping many items so that you know how far along the loop is.

The output will look like this if we had 3 Notepad processes:
[1/3]
[2/3]
[3/3]

We add some error handling to our loop with a Try Catch block.
The Stop-Process command has ErrorAction set to Stop which will make all errors “terminating errors” which will mean that Try Catch triggers on all errors.

If we encounter an error we write a warning with some useful information and add the Exception Message thrown by PowerShell, this is the red text information you get from an error.

Lastly we increase the $i variable by one.

Powershell has several more loops that we’ll discuss in the future.

And if you´re ever stuck in the loop, remember you only have to press CTRL+C 😉

What have loops solved for you in Powershell?

As always, continue to learn and evolve your skills, see you in the next one!

Do you want to know more? Here is a list of tutorials to check next