Tag Archives: PSItem

Proving PowerShell’s Usefulness to Newbies, Part I

Months ago, I started writing a list of ways to potentially impress PowerShell newbies for a presentation (I’ve yet to ever give). Since I’m not sure if I’ll be up in front of a crowd of PSNewbies any time soon, I figured I would share them here. Consider these when you need to impress the PowerShell non-believers.

Part I
The power in PowerShell has been said to be a number of different things, by a number of different people. In today’s topic, the power is speed and accuracy. We’re going to automate the creation of 10,000 directories (a.k.a. folders). That’s no small task if you’re doing it manually.

Beginning in the example below, we’ll start by creating a single directory to hold the other 10,000 directories, by running a New-Item command. Once that’s finished, which is practically instantaneous, we’ll run second command to create the 10,000 new folders. We’ll do this by using a range operator (..) and the numbers 1 and 10000, piping each of those numbers and the ones in between, to that second New-Item command.

Notice the $_ variable, as this has the tendency to cause some confusion. This variable holds the value of the current object that’s entered, or crossed, the pipeline — you pick your visual. In this case, it represents the current number from within our range of numbers. The first time this command runs, $_ will be set to 1, the second time this runs it will be set to 2, and the last time it runs, it will be set to 10000. I should note, that in PowerShell 3.0, the $PSItem variable was introduced. It can be used in place of $_ in version 3.0 and above.

PS> New-Item -Path C:\Testing -ItemType Directory

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/6/2016   9:52 PM            Testing

PS> 1..10000 | ForEach-Object {New-Item -Path "C:\Testing\$_" -ItemType Directory}

    Directory: C:\Testing

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/6/2016   9:52 PM            1
d----          1/6/2016   9:52 PM            2
d----          1/6/2016   9:52 PM            3
d----          1/6/2016   9:52 PM            4
d----          1/6/2016   9:52 PM            5
d----          1/6/2016   9:52 PM            6
d----          1/6/2016   9:52 PM            7
d----          1/6/2016   9:52 PM            8
d----          1/6/2016   9:52 PM            9
d----          1/6/2016   9:52 PM            10
...

I haven’t included all 10,000 results, for good reason, but as you can see, the moment the command was entered into the PowerShell host, it began to create the new folders. This would be the time at which you retie your shoes. The example further below indicates the command takes about 10 seconds to complete. Depending on your sneakers, if you’re wearing those, you might actually take longer. If you don’t have laces, take a drink of something, or simply sit there in amazement at all the work you’re not really doing. It might actually be too fast, once you’ve realized how much time you just freed up.

A little note about Measure-Command used below: Typically this cmdlet will internally measure things and not really do much that you can actually see. In this case, it’s actually going to create the folders as part of the measurement. To me, it makes sense that it wouldn’t be able to measure this unless it really creates the folders. On that note, if you ran the command above, you’ll want to delete the existing folders, if you want to measure how long it takes to create the folders. The New-Item cmdlet can’t create folders that already exist. To remove the directories, run:  Remove-Item -Path ‘C:\Testing\*’ from your PowerShell console.

PS> Measure-Command -Expression {1..10000 | ForEach-Object {New-Item -Path "C:\Testing\$_" -ItemType Directory}}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 10
Milliseconds      : 293
Ticks             : 102939103
TotalDays         : 0.000119142480324074
TotalHours        : 0.00285941952777778
TotalMinutes      : 0.171565171666667
TotalSeconds      : 10.2939103
TotalMilliseconds : 10293.9103

PS> Remove-Item -Path C:\Testing\*

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 9
Milliseconds      : 793
Ticks             : 97934962
TotalDays         : 0.000113350650462963
TotalHours        : 0.00272041561111111
TotalMinutes      : 0.163224936666667
TotalSeconds      : 9.7934962
TotalMilliseconds : 9793.4962

In the second command above, we actually measured the time it took to remove the directories we created — you saw that command a moment ago. It came in at a faster time than it took to create them, although this won’t always be the case. It’s still quite quick for either operation.

This is a great example of the power in PowerShell, but before I wrap up, let’s see how much time I just saved. Let’s assume it takes me 4 seconds to manually create a folder. To create 10,000 folders, I would need a free 40,000 seconds. To put that into hours, I would need over 11 of them to do this task by hand. We only get one person’s salary, but we should probably get a few.

PS> $SecondsToCreateFolder = 4
PS> $NumberOfFolderToCreate = 10000
PS> $NumberOfFolderToCreate * $SecondsToCreateFolder
40000
PS> $SecondsInMinutes = $MinutesInHours = 60
PS> $SecondsInMinutes
60
PS> $MinutesInHours
60
PS> ($NumberOfFolderToCreate * $SecondsToCreateFolder) / $SecondsInMinutes / $MinutesInHours
11.1111111111111

Thanks for reading this post.

Using the Range Operator for Calculating Total Push-Ups

In December of 2014, I decided that my life in 2015 was in need of some push-ups. Instead of just starting with 10 a day, or some other arbitrary number, I thought I would do as many push-ups a day as it was the day in the year. This meant that on day one (January 1, 2015), I would do one push-up and on day two, I would do two push-ups, and so on. Today is the 20th day of the new year, and so I’ll have to do 20 tonight. I wanted to know how many push-ups I will have done by January 31st. Being the Windows PowerShell hobbyist that I am, I enlisted PowerShell to do my calculations for me.

I started with a variable, $EndDay, and the range operator (..). The combination of the two provides me an integer array of the days in January, such as 1..$EndDay (or, 1..31). Using this, I can calculate how many total push-ups I will have done by the end of the day on January 31st. The example below sets up the integer array, as well as the ForEach-Object loop where we’ll do our calculations. Note: I’m using the ForEach-Object alias, foreach.

$EndDay = 31
1..$EndDay | foreach {

}

The first thing we do, below, is include a second variable, $PushUps, that will collect the total number of push-ups for the month. We’ll use the += assignment operator. This operator takes whatever is already in $PushUps, and adds to it. If the current value stored in $PushUps was 1, and we used the += assignment operator like so, $PushUps += 2, then the value in $PushUps would be 3 (1 + 2 is equal to 3). If we used the standard assignment operator (=), then $PushUps would be 2, as 1 would be overwritten.

On the next line, below, we write some information on the screen. We write the current day: that’s the current number from the integer array represented by $_ (as of PowerShell 3.0, $_ can be represented as $PSItem). Then we write out the total number of push-ups completed by that day: $PushUps.

$EndDay = 31
1..$EndDay | foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
}

I noticed that when I reran the code in the ISE, that the value of $PushUps was incorrect on the second run. This is because the variable already exists, and by the end of the first run already contains 496—the number of push-ups I’ll have done by the end of January! Therefore, I added an If statement that removed the $PushUps variable when $_ was equal to $EndDay. This happens on the final run through the foreach.

$EndDay = 31
1..$EndDay| foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
    If ($_ -eq $EndDay) {
        Remove-Variable PushUps
    }
}

If you change the value for $EndDay to 365, you’ll be able to determine that after December 31st (if I can somehow keep this up) I will have done 66,299 total push-ups for the year. It’s hard to imagine that I could do 365 push-ups at once, but then again, it’s hard to imagine I’ll get though the rest of the month. Here’s an image that shows the the full results when we run the function above.

Using the Range Operator for Push-Up Calculations

Thanks for reading, and wish me good luck—I’m going to need it.

Using Replace() to Fix Split() (and Convert-Path)

I was working on a recent project that required scripting ACLs, and so I had a Windows PowerShell console open in addition to the ISE. This allowed me to quickly check the owner of a directory (or folder). I could press the up arrow to rerun my command, and I could quickly see if the owner had changed according to my script. Let’s say my directory is called ‘TestFolder’ and is located at the root of the C:\ drive.

PS C:\> New-Item -Path TestFolder -ItemType Directory

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        10/21/2014   9:19 PM            TestFolder

To get the ACL information for the folder, you can use the Get-Acl cmdlet, as in the example below.

PS C:\> Get-Acl -Path .\TestFolder

    Directory: C:\

Path                                    Owner                                   Access
----                                    -----                                   ------
TestFolder                              BUILTIN\Administrators                  BUILTIN\Administrators Allow  FullCo...

Being the PowerShell enthusiast that I am, I modified my command so that only the information I wanted (the path and the owner) was being returned. This is done using the Select-Object cmdlet. Unfortunately, when the command returned those two properties, the Path property was no longer what I was expecting – take a look below. While the example above only returned the name of the folder (TestFolder), I thought I would change this to show the full path (C:\TestFolder) since I was now dealing with a string that included it.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property Path,Owner

Path                                                        Owner
----                                                        -----
Microsoft.PowerShell.Core\FileSystem::C:\TestFolder         BUILTIN\Administrators

The first thing I thought to do was to split the path at the two colons (::) and grab the second element, which I thought would end up being C:\TestFolder. The split method didn’t work so well; here’s what I ended up with.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={($PSItem.Path).Split('::')[-1]}},Owner

Path                                                        Owner
----                                                        -----
\TestFolder                                                 BUILTIN\Administrators

The reason this didn’t work is because the Split() method doesn’t split on each occurrence of two, back-to-back colons like you might expect, it splits on every colon. Since C:\ has a colon, it split there as well. FYI: The use of [-1] returns the last element in an array – good to know, I know. Here’s an example that may help better explain. In this example below, the string is split on every exclamation point (!) and every question mark (?) – not only on the combination of both (!?).

PS C:\> $String = 'Today is the 21st! That is great news, right?'
PS C:\> $String
Today is the 21st! That is great news, right?
PS C:\> $String.Split('!?')
Today is the 21st
 That is great news, right

PS C:\>

What I then decided to do to get this the way I wanted it, was to first replace the two, back-to-back colons with a single character (that was not a part of the string), and then split on that single character. It worked, and here’s what that looks like.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={(($PSItem.Path).Replace('::','@')).Split('@')[-1]}},Owner

Path                                                        Owner
----                                                        -----
C:\TestFolder                                               BUILTIN\Administrators

PS C:\>

It was about this point, that I wanted to see if the -split operator would have handled this the same way and required the additional work that the Replace()/Split() methods did. Of course, after all I did to get this to work how I wanted, I determined I should have started with the -split operator. The -split operator isn’t looking at the characters individually, but instead of, as a whole – two, back-to-back colons is two back-to-back colons.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={(($PSItem.Path) -split '::')[-1]}},Owner

Path                                                        Owner
----                                                        -----
C:\TestFolder                                               BUILTIN\Administrators

A note, if the @{N=…;E={…}} syntax is confusing, or new to you, then spend some time reading this: http://technet.microsoft.com/en-us/library/ff730948.aspx, and then run Get-Help about_Hash_Tables.

This happens just about every time I get to ready to publish a new post. I discovered a better way to handle the problem – much like I did when I considered the -split operator.  It turns out that I could have used a built-in cmdlet to convert the path for me. That’s right, there’s a cmdlet that would have handled everything. Using the Convert-Path cmdlet will convert something like this: Microsoft.PowerShell.Core\FileSystem::C:\TestFolder to this: C:\TestFolder. Here’s the example.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={Convert-Path $PSItem.Path}},Owner

Path                                                        Owner
----                                                        -----
C:\TestFolder                                               BUILTIN\Administrators

PS C:\>

Well, that’s it for this one. As much as I might seem irritated about how I did this three different ways – from the most work to the least – I understand how important this learning process is, and that one day I will be grateful for having gone down this path… (pun intended).