A crucial first step in learning programming is working with loops. Thankfully, PowerShell continues to grow with your skills.

You can frame the existing commands you use every day inside loops to save time and effort. Your scripts do the heavy lifting while you do the important work of reading more articles on MakeUseOf!

Powershell ForEach Loops: The Door to Advanced Data Handling

ForEach is the alias for the ForEach-Object. (An Alias is merely a shortcut for a command in PowerShell.) This is a good time to talk about the way that PowerShell handles data.

Like most modern programming languages, PowerShell is object-oriented. Everything in PowerShell is an object, meaning that even variables have extended properties and functions. That property is why you can set your searches to a variable, and end up with an array of the results.

        $yourVar = Get-ChildItem *
foreach ($file in $yourVar){
  Your Steps
}

In some languages, processing this array would be a multistep process. First, getting the length and then counting up each step.

In PowerShell, you step through the array and perform the action on each one using ForEach. This saves you several lines of code, which is helpful if you've got a longer script. For example, the following is a small script that would use a couple of  Powershell ForEach loops. It creates a ZIP archive of all your files that you haven't opened in 30 days.

Building a File Archive System Using ForEach Loops

Let's break the steps down. You use Get-ChildItem to get all the files in the Documents folder. The environment variable $env:USERPROFILE runs the script using the current profile. This variable is more portable than a hardcoded path. The results of that search are assigned to the variable $MyDocs. We then create our ForEach loop by having it step through each $Doc in $MyDocs.

        $oldDocs = @()
$MyDocs = Get-ChildItem -Path "$($env:USERPROFILE)\Documents" -Recurse
foreach ($doc in $MyDocs){
  if($doc.LastAccessTime -lt $(Get-Date).addDays(-30)){
    $oldDocs += $doc
  }
}
$ArchiveFolder = New-Item -Path "$($env:USERPROFILE)\Documents\$((Get-Date -Format MMddyy).toString())" -ItemType Directory
foreach ($doc in $oldDocs){
  Move-Item -Path $doc.FullName -Destination "$($ArchiveFolder.FullName)\$($doc.Name)" -Confirm $false
}
$source = $ArchiveFolder.FullName
$destination = "$($env:USERPROFILE)\Documents\$($ArchiveFolder.Name).zip"
Add-Type -AssemblyName "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
if(test-path $destination){
  Remove-Item -Path $ArchiveFolder -Recurse -Confirm $false
}

Inside the loop, we check to see if each file's LastAccessTime property is older than 30 days. We get this with the Get-Date cmdlet, and using the AddDays function with negative thirty. If it is, we add the file to the $myOldDocs array. After the file sort completes, we then take our completed array and create a zip file. This process is a little more complicated as it involves invoking a bit of .NET. Don't worry if you don't quite grasp it -- you can steal the code from this TechNet help document.

To break down what's happening here: We will move all of our old files to a new directory named for today's date for older than 30 days. Once that folder builds, we have to create the ZIP archive of the same name. We'll test to make sure that the archive succeeded and the .ZIP file is there, and then delete the new folder. Set this as a scheduled task to run once a month. You'll save yourself a little space and keep your Documents folder clean.

While and Do While: Loops on Condition

If you want to run a loop only when a particular condition is met, you use a While loop. If you're using a variable to track the count up, set that first.

        i=0
while(i<10){
  Your Steps
  i+=1
}

The problem is that if you aren't using a counter, you might want your code to run at least once even if the test is true. This is the case with the example script below. So in those cases, you want to use a Do-While loop. The syntax is slightly different.

        do{
  Your Steps
}while(Conditional Statement)

Using these is not quite as obvious to a novice programmer. Doing typical day to day scripting, you may not run into them that often. Where they especially come in handy is to make a makeshift timer for testing the success of a process.

We're going to build a quick script to reboot a remote machine and alert if it doesn't come back up within 15 minutes. This scenario assumes it's a home server or other machine that doesn't reboot very often. Feel free to adjust the time if your computer typically comes up faster.

Reboot and Check: Using a Do-While Loop

This script is a bit simpler. First, you use the Restart-Computer command to reboot the remote machine. (We used a dummy IP here for the reboot commands, be sure to overwrite this with your computer's DNS/IP). Then create the counter variable, i and set it to 0. Next, you have your Do loop with Start-Sleep stopping the script for 300 seconds (five minutes). A second command adds one to the counter.

        Restart-Computer -ComputerName 127.0.0.1
i=0
do{
  Start-Sleep -Seconds 300
  $i += 1
}while((!(Test-Connection 127.0.0.1 -Quiet)) -or $i -gt 3)
if($i -gt 3){
  Write-Ouput "Remote Machine not responding, please check."
}
else{
  Write-Output "Reboot Succeeded"
}

Then we have our While criteria. We use an Or test to ensure that a failure generates an alert. The alternative is the script looping endlessly waiting for the remote machine. To check for the machine, we are using the Test-Connection cmdlet. For simplicity, this is Ping for PowerShell. We add the parameter -Quiet which forces it to return True or False rather than the results of the packets. The second part of the Or statement checks if the counter is more than three.

Once the loop completes, we want to create the output. That means that we need to check our counter. This is a quick if/else statement. If it is greater than three, the script outputs that the remote machine isn't responding. If it isn't, it outputs that the reboot was successful.

Other Loops

There are two other kinds of loops available in PowerShell. They are somewhat related to the previous two loops, they just are not as commonly used. A For loop works similarly to the While example. You set all of your criteria in the evaluation, then set your cmdlets.

        for($i = 0;$i -lt 10;$i++){
  Your Steps
}

Do Until loops are like Do While loops, you just change the While statement to Until. In the example script, it would be the same as far as behavior. It is a style choice, but the Do While is more versatile in other situations. So if you only remember one, Do While is more useful.

powershell foreach loops

PowerShell has help for each of these loops as well. You can get the help by adding about before the loop name in Get-Help. You can then see examples and other tips for each type. These should be helpful if you get stuck.

Continuing to Grow With You

At this point, you have most of the skills to start building robust scripts. Whether automating your home rig or saving time at work, loops help your scripts to do more. Combining these loops with error handling moves your scripting beyond the basics. This opens the door to more advanced languages.

What is a clever PowerShell script you've created using loops? Share it with us in the comments.