| |
As part of my site redesign effort, I'm taking a look at different screen resolutions users of my site have (I'll post something on that later). I noticed a few strange screen resolutions and wanted to compare the different aspect ratios. I knew a couple of them, but not all, so I figured PowerShell should be able to solve this problem fairly easily to ultimately give me the following information. Note that the chart was created in Excel. I could've used WPF, but since this is a rat-hole of a rat-hole off my actual task at hand, the redesign, I didn't want to get into WPF generation in PowerShell.

Breaking the problem down, we need to be able to go from 1024x768 to 4:3. In order to do this, we'll have to whip out some elementary school math to figure out the greatest common divisor. Luckily, this is pretty simple to determine. First, let's start with getting a list of all divisors for one number.
function Get-Divisors($n)
{
$div = @();
foreach ($i in 1 .. ($n/3))
{
$d = $n/$i;
if (($d -eq [System.Math]::Floor($d)) -and -not ($div -contains $i))
{
$div += $i;
$div += $d;
}
};
$div | Sort-Object;
}
This is pretty simple, but I should probably cover a few non-obvious things. First, @() is an empty array, which will be used to store all of the divisors. Looking at the loop, you'll notice that I only loop thru one third of the possible values. I hope I remember this correctly from school, but you do not need to loop thru all numbers between 1 and the target value to identify all possible divisors. This is simply a way to speed up the calculation. I'll leave it to you to explore the algorithm on your own, since that's not my focus here. Within the loop, the function checks to see if the divisor is a whole number and whether the value has already been saved to avoid duplicates then adds each divisor to the array. Pay attention to this because what's happening here is that PowerShell sees [array] + [object] and automatically determins that you must want to add a new item to the array. This was a very nice surprise. Finally, Sort-Object puts the numbers in order for us humans. Now, we can get all divisors for a specific number.
PS C:\> Get-Divisors 1024;
1
2
4
8
16
32
32
64
128
256
512
1024
Next, we need to get the common divisors for both the height and width of the screen resolutions.
function Get-CommonDivisors($x, $y)
{
$xd = Get-Divisors $x;
$yd = Get-Divisors $y;
$div = @();
foreach ($i in $xd) { if ($yd -contains $i) { $div += $i; } }
$div | Sort-Object;
}
This is pretty much more of the same. Get all divisors, create a new array to hold the common divisors, loop thru the divisors to find the commonalities, and finally sort the array. The array shouldn't need to be sorted, since the previous function did it, but I figured it's probably good to be sure.
PS C:\> Get-CommonDivisors 1024 768;
1
2
4
8
16
32
32
64
128
256
Next, we'll grab the greatest common divisor.
function Get-GreatestCommonDivisor($x, $y)
{
$d = Get-CommonDivisors $x $y;
$d[$d.Length-1];
}
This isn't even worth explaining. Arguably, we could've simply returned the greatest common divisor in the last function, but this is a good way to create composable, reusable scripts. After all, we need to retrieve a list of divisors all the time, right? :-P I won't bother showing what the output would be. I'm sure you can figure this one out ;-) We'll move right on to the last step, which will get the actual aspect ratio.
function Get-Ratio($x, $y)
{
$d = Get-GreatestCommonDivisor $x $y;
New-Object PSObject -Property @{
X = $x;
Y = $y;
Divisor = $d;
XRatio = $x/$d;
YRatio = $y/$d;
Ratio = "$($x/$d):$($y/$d)";
};
}
This will most likely throw you for a loop, if you're a PowerShell beginner and possibly even some intermediate users. You most likely expected the function to simply return a string, like "4:3". We could absolutely do this, but with the richness of PowerShell, using a string is somewhat wasteful. This all comes back to a goal of composable scripts that can be reused in the future. In the future, we may want more than just a string value. Since PowerShell is so good at passing around objects, let's create a new object that has all the properties that make sense for this context, namely the two numbers, greatest common divisor, individual ratio portions, and the string representation of that ratio.
PS C:> Get-Ratio 1024 768;
Divisor : 256
Y : 768
XRatio : 4
X : 1024
YRatio : 3
Ratio : 4:3
Now, we have what we sought out to get: the aspect ratio for a 1024x768 screen resolution. Let's face it, tho, we didn't want one, we wanted a bunch of them. To be exact, I was curious about 10 different resolutions. We've gone this far to automate this process, we might as well finish up with a function to get a group of aspect ratios.
function Get-CommonRatios($res)
{
$ratios = @{};
foreach ($r in $res)
{
$rat = (Get-Ratio $r[0] $r[1]);
if (-not $ratios.Contains($rat.Ratio))
{
$ratios.Add($rat.Ratio,
(New-Object PSObject -Property @{
XRatio = $rat.XRatio;
YRatio = $rat.YRatio;
Ratio = $rat.Ratio;
Count = 1;
}));
}
else
{
$ratios[$rat.Ratio].Count += 1;
}
}
$ratios.Values | Sort-Object -Property XRatio;
}
Again, this is all pretty normal. I'm using a hashtable (@{}) instead of an array to avoid duplicates and also created a new object to hold the metadata instead of the original ratio object because not all of the properties on the old object are applicable anymore. I also added a property to count the number of times the aspect ratio is used.
PS C:\> Get-CommonRatios @((1024,768), (1280,800), (1280,1024), (1366,768), (1440,900), (1600,900), (1600,1200), (1680,1050), (1920,1080), (1920,1200)) | Format-Table Ratio, Count;
Ratio Count
----- -----
4:3 2
5:4 1
8:5 4
16:9 2
683:384 1
There ya have it. The only special thing I did was format the results as a table with only the ratio and count properties. Hopefully, you were able to pick up a few new things for me, I was glad to explore the dynamic array handling and runtime object creation.
There are a number of commands in PowerShell that aren't as "quiet" as you may want them to be. Sometimes, there are parameters to supress output, but not always. Fortunately, we have Out-Null. While seemingly simple, this is a priceless cmdlet. I use it when writing scripts to keep the output clean.
Perhaps the simplest explanation of this cmdlet is to show a very common function, md, which creates a new directory. For the uninitiated, this function is available to support backwards-compatibility to DOS. You may have caught that I referred to this as a function and not an alias to a cmdlet. Based on that, if you want to see what the function is, simply use Get-Content.
PS C:\Flanakin> Get-Content function:md
param([string[]]$paths); New-Item -type directory -path $paths
As you can see, md simply makes a call to New-Item and tells it to create a directory with the specified path. Pretty simple. Here's what the output looks like.
PS C:\Flanakin> md noisy
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Flanakin
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 1/27/2009 2:51 PM noisy
PS C:\Flanakin>
I don't know about you, but that's a lot more than I really care to know. Oh, and note the 6 extra lines. Bleh! Luckily, Out-Null will save us.
PS C:\Flanakin> md quiet | Out-Null
PS C:\Flanakin>
That's it! You gotta love something so simple.
Yes, I said "admins," but, unfortunately, this isn't an announcement. I'm simply thinking of a version of Visual Studio built for, well, admins. Specifically, I'm envisioning an environment to build and debug PowerShell scripts and cmdlets. Of course, this is only a hop, skip, and a jump away from PowerShell as an official .NET language. Imagine that, PoSh.NET... or, would someone force it to be P#? Either way, I like the idea. As an official language, that also opens it up to compiled scripts, which would be great for those servers without PowerShell installed. Of course, it's just a matter of time before PowerShell is default and the legacy DOS shell is eventually phased out. As a matter of fact, that's the plan for Windows 7 and Server 2008 R2. I'm not sure about Server Core, tho, since there's still the dependency on .NET.

I should start off by saying this isn't really about hard-core code generation. It's more about simplifying some of the more repetitive tasks we tend to do during development. In this instance, I needed to get 7 GUIDs to use in some test code. Sure, I could've use the Create GUID tool in Visual Studio, but what fun is that? Besides, I hate manual tasks and this tool would have forced me down a ~24 step process. No thanks. I'll stick to the 3 steps PowerShell can give me.
First thing's first, how do we create a GUID in PowerShell? I'm going to fall back to .NET for this one.
[Guid]::NewGuid()
If you run this, you'll get a blank line. What's up with that!? Admittedly, I'm not 100% sure why this is happening, but I have a pretty good guess. GUIDs are surrounded by curly-braces ({}), which are interpreted by PowerShell to be a script block. Putting 2 and 2 together, I'm assuming PowerShell thinks this is a script to run. Easy fix.
[Guid]::NewGuid().ToString()
That's it. The only other thing I had to do was add in my other formatting to create the GUID in code and slap that in a loop.
foreach ($i in 1..7) { 'new Guid("{0}")' -f [Guid]::NewGuid() }
You'll notice I opted to use the PowerShell shortcut syntax for string formatting. If you missed it, I talked about it as well as the static-method-calling syntax last week.
This tiny exercise reminded me of a pretty advanced Excel spreadsheet I created years ago because I was sick of typing the same bit of code over and over again. Since then, other tools, like GhostDoc and Resharper, have augmented my developer experience, enabling a much higher level of productivity than I had back then. Nonetheless, there is still room for improvement. This makes me wonder how much PowerShell could do for me with respect to code generation.

If you're doing any web or WPF/Silverlight work, you're probably used to dealing with the RGB (red-green-blue) hex color format. I can't tell you how many times over the years I've opened the calculator to convert to/from hex values to either get a specific value or find out what percentage is being used. I'm sure graphic artists deal with this a lot, because I know I find it relatively annoying when I've used apps in the past that only accepted 3 decimal numbers instead of the RGB hex format. That's changed over the years, however. I will say that this is perhaps worsened by WPF and Silverlight, which have incorporated opacity using the RGBA (red-green-blue-alpha) spec, which is arguably a misnomer, since the alpha channel (aka opacity or transparency) is specified first. All that aside, I found myself wanting to find out what a certain opacity was when working on a WPF app, so instead of opening the calculator, I referred to my handy-dandy PowerShell, which is always open on my desktop.
Being a .NET developer, I knew the Convert class has the ability to convert to/from hex, so I started out with this simple one-liner to convert a hex number to its decimal equivalent.
[Convert]::ToInt32("a6", 16)
To go the other way, you simply switch out the method name and first parameter you pass in.
[Convert]::ToString(166, 16)
The first parameter is the value to convert and the second value is the base (i.e. 2 for binary, 8 for octal, 10 for decimal, and 16 for hexadecimal).From a PowerShell perspective, there are a two more things to note here, since we're accessing a static method. First, you have to surround the class name with brackets ([]); and, second, you have to separate the class and method names with two semicolons (::). Also note that, for some classes, you may need to use the fully-qualified class name, which includes the namespace.
After doing this, I remembered the numeric format shortcuts available in C#. For hexadecimal numbers, you can reference a value without treating it as a string by prefixing it with 0x. So, to convert my hex number to decimal, I was able to drastically shorten the code (can you even call this "code?").
0xa6
To go the other way, we'll tap into standard string formatting logic. Knowing .NET, our first guess might be to just convert it to PowerShell, like we did before.
[String]::Format("{0:x}", 166)
Here, we're specifying a string format that renders the first parameter as hex as well as the number we're converting. This isn't saving us anything, tho. Luckily, we have a bit of PowerShell magic to shorten this for us.
"{0:x}" -f 166
I thought this was confusing the first time I saw it, but comparing it to the String.Format() method brings it home for me. Hopefully, for you, too.
I'm now converting the hex number, but that isn't telling me what the opacity is. This probably isn't even worth mentioning, because I know you're smart enough to figure this out for yourself, but we simply need to use a bit of division to get that percentage.
0xa6 / 0xff
I now know that A6 is 65%.
If you find yourself using the calculator every so often while working on something, consider keeping PowerShell in the background and just bringing it up instead. It's a great way to get used to the tool and even boost your productivity, as you get used to things you can do faster in PowerShell than via the mouse.

As you get into writing functions, you'll undoubtedly hit a scenario where a command may return an error. To buld the most robust scripts, you simply need to keep one standard argument in mind: -ErrorAction.
-ErrorAction tells PowerShell what to do if the command returns an error (obviously). There are four options: SilentlyContinue, Stop, Continue, and Inquire. As you most likely deduced, these continue without displaying the error, stop further processing, continue with the error displayed, and ask the user whether to continue, respectively. The default is Continue, but my favorite is SilentlyContinue because I would rather handle errors myself. If you're really lazy efficient, you can even use -EA 0. Since -ErrorAction is an enumeration, you can use this to specify any value, 0-3 (based on the previously mentioned order).
Sugrencia PowerShell: Control de Errores
En Español
Al crear funciones, usted encontrará un panorama cuando usted conseguirá un error. Para construir las escrituras robustas, recuerde un argumento: -ErrorAction.
-ErrorAction indica qué hacer si el comando devuelve un error (obviamente). Tiene cuatro opciones: SilentlyContinue, Stop, Continue, y Inquire. Como deduce lo más probable es que, estos continuar sin mostrar el error, detener el procesamiento, continuar con el error muestra, y preguntar al usuario si desea continuar, respectivamente. El estándar es Continue, pero mi favorito es SilentlyContinue porque tengo gusto de manejar mis errores. Si esta perezoso eficiente, puede utilizar -EA 0. Desde entonces -ErrorAction es un enumeración, puede utilizar este método para especificar cualquier valor, 0-3 (de acuerdo con la orden previamente mencionada).

So, my "daily" PowerShell tips haven't gone like I imagined. I have a list of topics to post. I just haven't had the time to type them all out. Either way, I'm still stuck on PowerShell. As a matter of fact, I just heard about a geeky website, Project Euler, which is dedicated to math and programming problems. When I first heard about it, I was thinking it sounded like a great way to hone your performance tuning skills. After trying the first problem, I'm not sure that's the case, but the problem was very simple. As a matter of fact, I solved it with a one-liner. If you're actually interested in solving it yourself, you may want to ignore this post. Then again, it's pretty simple.
I'll probably check out more of these. I'm not sure if I'll post the andwers or not, tho. Then again, as you progress, they get more and more complicated. If you're a sucker for pain, you can jump to one of the more complicated problems. I don't know how hard they'll be, but I'm sure it'll take longer than the 2 minutes this one took me.
Problem 1
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
My Solution
All I did was loop tthrough all numbers and added the number to a running tally if it was divisible by 3 or 5. Gee, that's pretty much what the problem statement was. Like I said, this one is pretty simple.
$sum = 0; ForEach-Object ($i in 3 .. 10) { if ((($i % 3) -eq 0) -or (($i % 5) -eq 0)) { $sum += $i } }; Write-Output $sum
What really surprised me was that this script ran from 3 to 1000 in 0.0156 seconds. I don't know what typical numbers are, but I was expecting it to take longer. For learning purposes, the aliases for ForEach-Object and Write-Output are foreach and echo.

It's amazing what sticks and what doesn't. Back in Aug 2004, I caught wind of UpdateVersion, a tool Matt Griffith wrote to update version numbers in AssemblyInfo files. The tool is pretty simplistic, but provides an absolute benefit. Every couple of months, I get asked for a copy of the changes I made... despite the fact that they've been available online for years. Nonetheless, it's about time I created a project on CodePlex for the utility. At this point, I don't really expect to make any changes to it, but I will if someone sees value in it. If I were to make any changes, I'd probably go ahead and convert it to .NET 3.5 and possibly even add a PowerShell cmdlet.

I just wanted to share a small script that creates a new profile and registers the ps1 file extension. For those that don't know, ps1 is the default extension for PowerShell scripts. Of course, there's a difference between ps1 and bat or cmd. If you double-click a ps1 file in Windows Explorer, it'll open in Notepad. What's up with that!? Apparently, this was done for security reasons. The idea is that, since you can't simply double-click on the file to execute it, hopefully you'll actually look at it to make sure it's not going to kill your system. PowerShell is much more dangerous than traditional batch files are, so this is probably a good thing. With that in mind, PowerShell, by default, doesn't allow you to even execute these ps1 files. To do that, you have to set the execution policy. Anyway, here's the script...
$dir = [System.IO.DirectoryInfo]$profile
New-Item -Type Directory -Path $dir.Parent.FullName
Set-ExecutionPolicy RemoteSigned
I found this online a while ago, but I don't remember where. The only other thing I want to mention, since I imagine some people might freak out by it is the New-Item cmdlet, is that there's a function that simplifies this call and gives us a familiar DOS experience: md. I always thought md was an alias, but never bothered to consider why/how the New-Item cmdlet was determining that you wanted a directory.
Is PowerShell the best command line environment out there? Looking at a comparison of computer shells, I'm thinking it is. I'm sure others would disagree, but the facts are there. There are about 3-5 runners up, but the one feature which puts PowerShell over the top, in my mind, is the use of .NET objects in the pipeline.
La Mejor Línea de Comando
En Español
¿Es PowerShell la mejor línea de comando? Después de mirar una comparación sobre cáscaras de las computadoras, pienso que es. La otra gente discrepará probablemente, pero los hechos están allí. Hay 3 a 5 cáscaras segundarias, pero la característica que PowerShell el mejor es el uso de objetos de .NET en la tubería.
|
|
|