Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

Weekend Scripter: Manipulating Word and Excel with PowerShell

$
0
0

Summary: Leverage the Office API to manipulate data in Word and Excel with Windows PowerShell.

Honorary Scripting Guy, Sean Kearney, is here today to give Ed the day off. Why? Well, it's his birthday, of course! People worldwide have been sending Ed Happy Birthday wishes. His inbox almost went offline with so many "Happy Birthdays" coming his way!

Photo of Ed Wilson

Today Ed is relaxing, but he has one task. The Scripting Wife is standing over him, "Ed, you should send thank-you notes to all of your friends."

The Scripting Guy looked at his inbox, "I'd love to, dear, but there are almost 87,561 people wishing me Happy Birthday. If I was to do a quick Reply All and BCC them, Microsoft would shut down my email account on account of spam! Dude!"

She looked at him, "Don't you 'Dude' me Mr. Scripting Guy. You know perfectly well this is a great opportunity for you to use Windows PowerShell. I've even gone to the trouble of organizing everything in a Microsoft Excel spreadsheet with names and street addresses."

Ed paused. "Did you say street addresses?"

"Yes, that's right dear," she paused for effect. "You're going to mail them all a nice little letter. That will keep your boss happy and the postal system even happier. Here's a little letter I typed in Word that you can use."

***FirstName*** ***LastName***
***StreetAddress***
***City*** ***State***
***Country***

Hey, ***FirstName***, this is Ed, the Scripting Guy!

I just wanted to thank you personally for wishing me a Happy Birthday.

I thought it was really cool how you took the time out from your day in ***Country*** to brighten up my day! I wrote this letter with a really cool Windows PowerShell script that works with Microsoft Excel and Word!

Thanks a bundle and keep on scripting!

Ed Wilson
The Scripting Guy

"I've also prepped all of your friends in an Excel spreadsheet that contains details in this format:"

FirstName         LastName          StreetAddress    City       State     County

Ed thought for a moment, "You know, I could use Word MailMerge to solve this issue too."

The Scripting Wife looked at him. "What, you’d waste a perfectly good reason to use Windows PowerShell and have some fun while you're doing it”

Our good friend smiled over at her, "You know me so well!"

The first challenge Ed had to overcome was calling up Word from Windows PowerShell. This wasn't so tricky with a very simple function to hide all the work and return the opened object to be manipulated:

Function OpenWordDoc($Filename)

{

$Word=NEW-Object –comobject Word.Application

Return $Word.documents.open($Filename)

}

With this in place, Ed could simply call the function in this manner to open a Word document for editing:

$Doc=OpenWordDoc -Filename "something.docx"

Next, Ed sat down and worked with the Search and Replace features in Microsoft Word. The Scripting Wife had created a document with key identifiable fields beginning and ending with ***, so he decided to search and replace on those with the final data. With a little digging in the TechNet Gallery, he found a script that provided a decent example: Find and Replace Text in a Microsoft Word Document.

Function SearchAWord($Document,$findtext,$replacewithtext)

  $FindReplace=$Document.ActiveWindow.Selection.Find

  $matchCase = $false;

  $matchWholeWord = $true;

  $matchWildCards = $false;

  $matchSoundsLike = $false;

  $matchAllWordForms = $false;

  $forward = $true;

  $format = $false;

  $matchKashida = $false;

  $matchDiacritics = $false;

  $matchAlefHamza = $false;

  $matchControl = $false;

  $read_only = $false;

  $visible = $true;

  $replace = 2;

  $wrap = 1;

  $FindReplace.Execute($findText, $matchCase, $matchWholeWord, $matchWildCards, $matchSoundsLike, $matchAllWordForms, $forward, $wrap, $format, $replaceWithText, $replace, $matchKashida ,$matchDiacritics, $matchAlefHamza, $matchControl)

}

This function would take the open Word documents and pass in a field to find the data to replace as follows:

SearchAWord –Document $Doc -findtext 'something' -replacewithtext 'anotherthing'

His next challenge was to save this document under a brand new name. To do this, he called up the object from the existing open document and targeted the SaveAs method. Again our good friend used a simple function to make it a bit more seamless:

Function SaveAsWordDoc($Document,$FileName)

{

$Document.Saveas([REF]$Filename)

$Document.close()

}

Now Ed could simply save the file under a new name in the following manner:

SaveAsWordDoc –document $Doc –Filename $Savename

Our dear friend went to convert the Excel document to a CSV file to allow for a quick import with Import-CSV when The Scripting Wife tapped him on the shoulder, "I know you, and you are fully capable of calling up Excel directly. Let's see those scripting skills at work."

Ed smiled. Of course, more cool Windows PowerShell to read Excel directly. He clapped his hands together. Two quick functions to open and close the Excel document:

Function OpenExcelBook($FileName)

{

$Excel=new-object -ComObject Excel.Application

Return $Excel.workbooks.open($Filename)

}

 

Function SaveExcelBook($Workbook)

{

$Workbook.save()

$Workbook.close()

}

Ed would now be able to access any Excel spreadsheet in Windows PowerShell by calling up these new functions as follows:

$Workbook=OpenExcelBook –Filename "SomeExcelSheet"

SaveExcelBook –workbook $Workbook

The only thing our good friend needed now was a way to read the data in Excel. With one more function in Windows PowerShell, he was ready to go:

Function ReadCellData($Workbook,$Cell)

{

$Worksheet=$Workbook.Activesheet

Return $Worksheet.Range($Cell).text

}

Ed could now call up this function to pull in data from any cell in the default worksheet in the Excel workbook created by the Scripting Wife. For example, this function would return the value of Cell A1 into the object named $Data:

$Data=ReadCellData -Workbook $Workbook -Cell "A1"

Ed was giggling like a kid in a toy shop, "Woohoo! I can now run a Do Until loop and create massive Word documents until I run out of Excel data! Dude!"

$Workbook=OpenExcelBook -FileName 'C:\HSG\HappyBirthday.xlsx'

$Row=2

Do

{

$Data=ReadCellData -Workbook $Workbook -Cell "A$Row"

If ($Data.length –ne 0)

{

$Doc=OpenWordDoc -Filename "C:\HSG\HappyBirthday.docx"

SearchAWord -Document $Doc -findtext '***FirstName***' -replacewithtext $Data

$Data=ReadCellData -Workbook $Workbook -Cell "B$Row"

SearchAWord -Document $Doc -findtext '***LastName***' -replacewithtext $Data

$Data=ReadCellData -Workbook $Workbook -Cell "C$Row"

SearchAWord -Document $Doc -findtext '***StreetAddress***' -replacewithtext $Data

$Data=ReadCellData -Workbook $Workbook -Cell "D$Row"

SearchAWord -Document $Doc -findtext '***City***' -replacewithtext $Data

$Data=ReadCellData -Workbook $Workbook -Cell "E$Row"

SearchAWord -Document $Doc -findtext '***State***' -replacewithtext $Data

$Data=ReadCellData -Workbook $Workbook -Cell "F$Row"

SearchAWord -Document $Doc -findtext '***Country***' -replacewithtext $Data

$SaveName="$FirstName-$LastName.docx"

SaveAsWordDoc –document $Doc –Filename $Savename

$Row++

}

SaveExcelBook –workbook $Workbook

}

Ed saved and ran his little script as HappyBirthday.ps1. Within moments, he was staring a directory structure populated with Word documents for his many friends.

Moments later with a drag-and-click, the documents were all printed out—mountains of paper filling the sky and pouring out on the ground.

"Oh dear…" Ed looked at the Scripting Wife, "Because it's my birthday, can you stuff all of these into envelopes for me?"

Rumor has it that noone has ever moved quite so fast as Ed dodging the look he got after that question.

Happy Birthday, Ed! Enjoy your day off!

Image of cake

…from all of your friends in the scripting community!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send an email to the Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, remember, the Power of Shell is in You.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy 


PowerTip: Use Windows PowerShell to Open Word Document

$
0
0

Summary: Learn how to use Windows PowerShell to open a Microsoft Word document.

Hey, Scripting Guy! Question How can I open a Word document with Windows PowerShell?

Hey, Scripting Guy! Answer Use the New-Object cmdlet:

$Filename='C:\HappyBirthdayEd.docx'

$Word=NEW-Object –comobject Word.Application

$Document=$Word.documents.open($Filename)

You can now programmatically access methods and properties to edit the Word document.

Use PowerShell to Start a Process at Random Times

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to start a process at random intervals.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a rather unusual request. I am testing a new monitoring application, and I need a script that will start processes at random intervals so I can check the effectiveness of the solution. Is this something you can help me with?

—BB

Hey, Scripting Guy! Answer Hello BB,

Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sitting on the porch, sipping a nice cup of green tea with orange flavorings in it. The tea is called Orange Cream, and it tastes exactly like the old Dreamsicles I used to eat when I was a kid. It is a different way to start the morning. I am cooling off after spending an hour in the gym. I have my Surface Pro 3 that the Scripting Wife got for me for my birthday (it was an early birthday present…I have had it for awhile now), and I am catching up with email sent to scripter@microsoft.com and Facebook. I don’t know about you, but lately, it seems like I am always multitasking. I find it hard to sit and do just one thing at a time anymore. I’m not sure if this is a good thing or a bad thing.

Picking out tools

BB, there are many ways to write a Windows PowerShell script that will generate new processes at random intervals. But when I hear this requirement, I think that I will need the following:

  • Get-Random  To generate random numbers. I also know that I will need to specify minimum and maximum values. I will accept the default seed.
  • Start-Process  To start the processes and give me a bit of control over the way they start. For testing purposes, I probably want to use a minimized window style so that it does not block other things (such as the Windows PowerShell ISE for example), and so I will still be able to see that the processes have in fact started. Obviously, I will need a way to specify the program name (and path if needed). I can do this with a variable.
  • Start-Sleep  To control the amount of time between the startup of the process. I will use the –seconds parameter and use the number I get back from Get-Random to control the sleeping. This also means that I will need to use numbers large enough to be minutes instead of seconds (for example, 300 for five minutes).
  • Whilestatement  To put the script in an infinite loop. I am only testing the script at this point, so this will work well because I am sitting here monitoring it. Later I may want to increase my minimum and maximum random values, and run the script over a twelve hour period of time. For this, I will use the For statement and control exactly how many times I want the script to execute.

Writing the script

Because I did a good job analysis, and I know pretty much all of the commands I am going to use in the script, writing the script does not take me very long at all. In fact, here is the script I wrote:

   Note  Using While ($true) puts the script into an infinite loop. To stop the loop, press the red square (stop button) in the Windows PowerShell ISE.

# StartProcessesAtRandomTimes.ps1

$prog = "notepad.exe"

while ($true)

{

 $rnd = Get-Random -Minimum 1 -Maximum 5

 Start-Sleep -Seconds $rnd

 start-process -FilePath $prog -WindowStyle Minimized}

Testing the script

To test the script, I load it in the Windows PowerShell ISE, press the green triangle, and then wait for a while. Nothing appears, and nothing appears to happen. But, at least on my laptop, I see the screen flash every once in a while, and on my task bar, I see several Notepads stacked up. So I press the red square, and stop the script. Still nothing appears to have happened—at least not from looking at the Windows PowerShell ISE. Here is the screenshot:

Image of command output

In the interactive pane, I use Get-Process to find all the Notepad processes. As shown here, there were quite a few generated:

Image of command output

That is cool. Now I want to see if my randomizer is working—that is, at what time were the Notepad processes generated. That is easy enough. I use the StartTime property. It is probably best to also sort them. So I use the following script:

PS C:\> Get-Process notepad | Select StartTime | sort  -Descending

The problem is that it does not sort properly. It looks like it is sorted, but I try the ascending and the descending sorts, and I see the order does not change. Dude.

I then realize that I need to expand the DateTime object (that is contained in the StartTime property) so I can specify WHAT I want to sort on. Obviously, I am interested in the TimeOfDay. So I revise my command to the following:

Get-Process notepad | select -expand StartTime | sort timeofday    

Now I try it, and I see that it is working. In fact, I am getting random offsets between the process starts. This is shown here:

Image of command output

BB, that is all there is to using Windows PowerShell to create processes at random intervals. Process Week will continue tomorrow when I will talk about launching random processes at random times.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Find Path for Processes

$
0
0

Summary: Use Windows PowerShell to find the path of the executable for running processes.

Hey, Scripting Guy! Question I noticed a process and I am unsure of what it does. How can I use Windows PowerShell to find more information?

Hey, Scripting Guy! Answer Look at the path to the executable to find information about a process. If it is legitimate, often the folder
           provides decent information. Here is an example:

(Get-Process -Name snagit32).path

Use PowerShell to Start Random Processes at Random Times

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to create random processes at random times.

Hey, Scripting Guy! Question Hey, Scripting Guy! I need to start a process that I pick randomly from a list. I would like these processes to start at random times so I can test my monitoring application. Is this something Windows PowerShell can do?

—JD

Hey, Scripting Guy! Answer Hello JD,

Microsoft Scripting Guy, Ed Wilson, is here. This morning, I am sipping a cup of Darjeeling tea that I made with spearmint, licorice root, and a cinnamon stick. The taste is understated, and quite nice. It goes well with the Irish steel-cut oats and blueberries that I am having for breakfast. I am sitting in the kitchen this morning (it is raining outside) and eating at the counter. I have my Surface Pro 3 with me, and I am checking email sent to scripter@microsoft.com.

Create random processes the easy way

If you really want the path to an executable that is random, the easy way to do it is to use the Get-Command cmdlet, and search for *.exe. This will return a CommandInfo object that has a Path property. I would then feed that to the Get-Random cmdlet as an InputObject. Here is a command that would work for this:

Get-Random -InputObject (Get-Command *.exe) | Select path

The output displays the complete to the executable. Here is an example:

PS C:\> Get-Random -InputObject (Get-Command *.exe) | Select path

Path

----

C:\WINDOWS\SYSTEM32\gpresult.exe

A safer approach

The issue with the previous approach of gathering really random executables is that if I later decide I want to launch the executables, I could really get some dangerous and unpredictable results. A safer approach is to create an array of executable names, use Get-Command to find the path, and send that to Start-Process. In this way, I can ensure that I am only starting processes that I want to start. Not really random processes, but launching known processes in a random order.

Because I need to create an array of strings, it would normally mean using a lot of quotation marks and commas, and extra typing. I then assign it to a variable to hold the array. But I was recently watching Todd Klindt’s Netcast, and he used a trick that I had forgotten about. I have added it back to my repertoire. The secret is to create a string that contains commas, and then use the Splitmethod to create the array. It is simple and fast. Here is an example of what I am talking about:

First the old way:

$a = "mspaint.exe","write.exe","calc.exe","notepad.exe"

Now for the new way:

$a = "mspaint.exe,write.exe,calc.exe,notepad.exe".Split(',')

I like doing this because although it is a little bit more typing (only a couple extra key strokes), it is easier to type. I don’t like having to hold down the Shift key and then pressing the quotation mark. (Actually, if I include Shift in my keystroke count, it is a real savings in typing.)

Getting the random process names from the array

It is possible to index into the array to get the process names. To do this, I might use a command such as the following:

PS C:\> $a

mspaint.exe

write.exe

calc.exe

notepad.exe

PS C:\> $r = Get-Random -Maximum 3 -Minimum 0

PS C:\> $a.Item($r)

mspaint.exe

But that is cumbersome, and it uses extra lines of code and extra variables. In addition, if I later add additional executable names to the array, I have to update the Get-Random portion (an easy thing to forget). A better way to do this is to pass the array of executable names directly to Get-Random and let it handle the details. Here is the command:

Get-Random -InputObject $a

To make sure I have the path to the executable, I call the Get-Command cmdlet and select the path.

   Note  I could simply type the complete path to the executable in my array, but that is a lot of extra typing, and it is
   error prone. Using Get-Command is much simpler.

Here is the command I use to get the path to my randomly selected executable:

Get-Random -InputObject $a | Get-Command | select path

Unfortunately, the Start-Process cmdlet does not accept pipelined input. So I need to use a Foreach-Object cmdlet prior to calling the Start-Process cmdlet. So I use Get-Random to select random executable names, get the path by using Get-Command, and pass it to Start-Process. Here is the command:

Get-Random -InputObject $a | Get-Command | Foreach-Object {Start-Process $_.path}

Groovy…now put it with yesterday’s script

I am now ready to put my new command into the script I wrote yesterday in Use PowerShell to Start a Process at Random Times.  

After I do that, I will launch a randomly selected process from an array of executable names, and I will do so at random intervals.

Here is the complete script:

$prog = "mspaint.exe,write.exe,calc.exe,notepad.exe".Split(',')

while ($true)

{

 $rnd = Get-Random -Minimum 1 -Maximum 5

 Start-Sleep -Seconds $rnd

 Get-Random -InputObject $prog |

 Get-Command |

 ForEach-Object {

   start-process -FilePath $_.path -WindowStyle Minimized } }

   Note  Calculator does not start if it is minimized, but the other three processes will. So be aware of this prior to
   running the script.

JD, that is all there is to using Windows PowerShell to create random processes at random times. Process Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Change Value of Element in Array with PowerShell

$
0
0

Summary: Learn how to use Windows PowerShell to change the value of an element in an array.

Hey, Scripting Guy! Question How can I use Windows PowerShell to update one of the values in one of the elements of an array?

Hey, Scripting Guy! Answer Use the Item method to retrieve the element and assign a new value. Here is an example of
           updating the value of the first element in an array:

$a = 1,2,3,4,5

$a.Item(0) = 12

$a

Note  Remember that in Windows PowerShell, all arrays are zero-based. This means that the first
element is actually element 0.

Use PowerShell to Monitor for Process Startup

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to monitor for process startup.

Microsoft Scripting Guy, Ed Wilson, is here. This morning, I spent a decent amount of time answering an email from a former colleague. I will actually share his email and my reply this coming weekend. In the meantime, I was playing around Register-CimIndicationEvent. This is not really a new Windows PowerShell cmdlet—I mean, it has been around for a while (since Windows PowerShell 3.0), but not much has been written about it. There is little useful documentation for the cmdlet either.

In today’s post, I will talk about the following Windows PowerShell cmdlets:

  • Register-CimIndicationEvent
  • Get-Event
  • Get-EventSubscriber
  • Unregister-Event
  • Select-Object
  • Remove-Event

To monitor for any process startup, I can use the WMI class Win32_ProcessStartTrace. When the event fires, the class provides information about the process ID, the parent process ID, the process name, the session ID, and even the SID for the user context that generates the event. This information becomes available after I retrieve the generated event. For more information, see MSDN: Win32_ProcessStartTrace class.

Register for the event

Note  This procedure requires admin rights. To start an elevated Windows PowerShell console, right-click the Windows PowerShell console icon and select Run as Administrator from the action menu.

The first thing I need to do is to register to receive events. To do this, I use the Register-CimIndicationEvent cmdlet. When I do, I want to specify the WMI class to monitor and a name that I can use to track the generated event subscription. Here is the command I use:

Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace -SourceIdentifier "ProcessStarted"

When I run the command, nothing returns. But I can use the Get-EventSubscriber cmdlet to verify that something actually happened (such as creating a new event subscription). Here is the command and its associated output:

PS C:\> Get-EventSubscriber

SubscriptionId   : 7

SourceObject     : Microsoft.Management.Infrastructure.CimCmdlets.CimIndicationWatcher

EventName        : CimIndicationArrived

SourceIdentifier : ProcessStarted

Action           :

HandlerDelegate  :

SupportEvent     : False

ForwardEvent     : False

Hey, is anything happening?

I have not deliberately started any processes yet (like Calculator or Notepad), but when I use the Get-Event cmdlet, I see that the operating system generated a bunch of processes anyway. So there are already new processes starting up. The command with no parameters is shown here:

Get-Event

Here is what the output looks like at this point:

Image of command output

There is one property that I can basically understand and that is the TimeGenerated property. Everything else is wrapped up in other objects. One thing I can do is take advantage of the ability of Windows PowerShell to do what I call “the automatic foreach.” That is, I can select a specific property from a collection of objects, and Windows PowerShell will display that object. Here, I choose the SourceEventArgs property:

PS C:\> (Get-Event).sourceeventargs

NewEvent               MachineId              Bookmark              Context

--------               ---------              --------              -------

Win32_ProcessStartT...

Win32_ProcessStartT...

Win32_ProcessStartT...

Win32_ProcessStartT...

Win32_ProcessStartT...

<output truncated>

Notice that each NewEvent says that it is an instance of Win32_ProcessStartTrace. Remember the MSDN document I mentioned earlier? This is where that comes into play. I can now cherry pick which of the properties of Win32_ProcessStartTrace I want to see. First, I look at the NewEvent property that contains instances of Win32_ProcessStartTrace. Here is the result:

PS C:\> (Get-Event).sourceeventargs.newevent

SECURITY_DESCRIPTOR :

TIME_CREATED        : 130552887343479772

ParentProcessID     : 4512

ProcessID           : 9304

ProcessName         : SearchProtocolHost.exe

SessionID           : 1

Sid                 : {1, 1, 0, 0...}

PSComputerName      :

SECURITY_DESCRIPTOR :

TIME_CREATED        : 130552887343479773

ParentProcessID     : 4512

ProcessID           : 3540

ProcessName         : SearchFilterHost.exe

SessionID           : 0

Sid                 : {1, 1, 0, 0...}

PSComputerName      :

<output truncated>

Yep. That is what I thought would be there. So now let me get a list for only the ProcessName property to see what is actually starting up:

Image of command output

Cool. But when are these events happening? If I look at the Time_Created property, all I get is a bunch of numbers. When I look at MSDN, it simply says it is a string that represents time created—not exactly the most helpful Help in the world. Here is the output:

PS C:\> (Get-Event).sourceeventargs.newevent.Time_Created

130552887343479772

130552887343479773

130552887430300826

130552889482027640

130552889482027641

<output truncated>

So, I decide to try one of my favorite tricks to translate this. I use the [System.Management.ManagementDateTimeConverter] class. But no matter what I try, I get error messages. Here are some efforts that didn’t work:

[System.Management.ManagementDateTimeConverter]::ToDmtfdatetime(130552788310990332)

[System.Management.ManagementDateTimeConverter]::Totimespan(130552788310990332)

[System.Management.ManagementDateTimeConverter]::Todatetime(130552788310990332)

So basically, I give up?

No, I don’t give up—not with Windows PowerShell. There is always a way to do what I need to do when using Windows PowerShell. I decide to pull out another one of my favorite tricks: the custom property with Select-Object. Remember that earlier when I was looking at the output from Get-Event, I saw the TimeGenerated property.

This property comes from Get-Event and is logged when the event generates. It does not come from WMI, so it is a proper DateTime object. I need to pick the property from this value and then get the process name from the object held in the NewEvent property. Here is the command I decided to use (this is a one-line command that I broke into two lines for readability online):

Get-Event |

select timegenerated, @{L='e'; E = {$_.sourceeventargs.newevent.processname}}

The output from this command is shown here:

Image of command output

Cleanup work

Now I need to do a little bit of cleanup. First, I remove all of the events. To do this, I use Get-Event and pipe the output to Remove-Event. I next confirm that this worked by using Get-Event:

get-event | Remove-Event

get-event

Second, I need to unregister the event subscribers. To do this, I use Get-EventSubscriber and pipe the output to Unregister-Event. Once again, I call the prior command to confirm. Here are the two lines of script:

Get-EventSubscriber | Unregister-Event

Get-EventSubscriber

That is all there is to using Register-CimIndicationEvent to monitor for process startup. I will continue talking about processes tomorrow when I will talk about more groovy things.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find Extended Type Data in PowerShell Session

$
0
0

Summary: Find extended type data in your current Windows PowerShell session.

Hey, Scripting Guy! Question How can I find out what extended type data exists in my current Windows PowerShell session?

Hey, Scripting Guy! Answer Use the Get-TypeData cmdlet.


Use PowerShell to Monitor Specific Process Creation

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to monitor for the creation of specific processes.

Microsoft Scripting Guy, Ed Wilson, is here. This morning it is beginning to look like autumn. Who knows, it may become really hot and humid over the weekend, but today I can delude myself into hearing the rustling of leaves, imagine cool breezes blowing across the yard, and think of squirrels as they quickly gather supplies for a long winter.

All of these things are really quite simple, routine, and ordinary. And yet they demand a certain amount of preparation. If I don’t clean the leaf troughs in the early autumn, winter rains and subsequent freezing could spell disaster for the roof. If I don’t roll up hoses and bring them into the garage, pipes could freeze. If the squirrels don’t gather enough supplies for a long winter, they will be out scampering in the snow and risk untoward dangers.

And so it is with our computer systems—they demand monitoring, planning, and preparation for contingencies.

In yesterday’s post, Use PowerShell to Monitor for Process Startup, I talked about using the Register-CimIndicationEvent cmdlet to monitor for process startup. It worked well—maybe a bit too well because it triggers an event for every new process that starts. And in a modern operating system like Windows 8.1, there are all kinds of processes that start and stop all the time.

Today I want to use a query to modify the behavior of Register-CimIndicationEvent. To do this, I will use the Windows Management Instrumentation Query Language (WQL). Don’t worry. Because I am using the Win32_ProcessStartTrace WMI class, this will be really easy.

Note  For more information about WMI event monitoring, see An Insider’s Guide to Using WMI Events and PowerShell.

Here are the steps I will take to permit me to monitor for the startup of a specific process:

  1. Open the Windows PowerShell console with elevated rights.
  2. Create a query that uses WQL syntax.
  3. Register to receive events by using the Register-CimIndicationEvent cmdlet and supplying the query.
  4. Use Get-Event to receive the events.

In addition, I will use the Get-EventSubscriber cmdlet to verify that the event was created properly, and I will use Remove-Event and Unregister-Event to perform cleanup.

Create the query

I first create the query. It looks pretty much like a SQL query. I select everything from the win32_ProcessStartTrace WMI class, and I limit the results to processes that are named Notepad.exe.

Note  When I use Get-Process it tells me the ProcessName property is equal to notepad. But WMI expects the ProcessName property to be equal to notepad.exe. Keep in mind, no error generates if I use notepad, but I will not receive any events either.

I store my query string in a variable that I call $nq (for notepad query). Here is the query:

$nq = "Select * from win32_ProcessStartTrace where processname = 'notepad.exe'"

Register to receive events

Now I use the Register-CimIndicationEvent cmdlet to register to receive the events I defined in my query. I also specify a SourceIdentifier that I call nq. This makes it easy to receive only events generated by my specific notepad query. Here is the command:

Register-CimIndicationEvent -Query $nq -SourceIdentifier nq

Quick check

Now I do a quick check. Did my registration work? I use Get-EventSubscriber. Are there any events? (There should not be at this point.) I use Get-Event.

Here is what my Windows PowerShell console looks like at this point:

Image of command output

Generate and receive events

Now I generate an event by launching Notepad. When I do that, I use the Get-Event cmdlet to receive the event. Here is the command:

Get-Event -SourceIdentifier nq

The returned object contains a number of properties. These properties are shown in the image that follows:

Image of command output

I know, from yesterday’s post, that the information I am interested in obtaining is contained in the SourceEventArgs property and in the NewEvent property under that. I use dotted notation to gain access to the important properties:

Image of command output

That was fun, do it again

That worked well. I can see that with the ProcessID I received, I could manage the newly created process if I needed to do so. But what about monitoring for an additional process? One way is to simply create another event registration. This time I will do it for Calc.exe. Here is the command:

$cq = "Select * from win32_ProcessStartTrace where processname = 'calc.exe'"

Register-CimIndicationEvent -Query $cq -SourceIdentifier cq

Now I launch Calculator (calc.exe) and retrieve the event. Here is the command that does that:

calc

(Get-Event -SourceIdentifier cq).SourceEventArgs.newevent

This command and the output from the command are shown in the following image:

Image of command output

It still works with Notepad also. I simply use the nq source. I decide to clean up everything. Here are the commands I use:

get-event | Remove-Event

Get-EventSubscriber | Unregister-Event

Now there are no more events and no more event subscriptions.

A combined query

I can, of course, combine my two event queries into a single query. To do this, I use a compound where clause. Here is my new query (this is a single line query that wraps due to the length. It includes no returns or line continuation marks):

$q = "Select * from win32_ProcessStartTrace where processname = 'calc.exe' OR processname = 'notepad.exe'"

I register for events from this query just like I did for the other queries. Here is the command:

Register-CimIndicationEvent -Query $q -SourceIdentifier q

Now I launch both Notepad and Calculator, and I use Get-Event to retrieve my new events. Here is the command that does that:

notepad

calc

(Get-Event -SourceIdentifier q).SourceEventArgs.newevent

The commands and their associated output are shown here:

Image of command output

That is all there is to using the Register-CimIndicationEvent cmdlet to monitor for a specific process. Join me tomorrow when I will talk about terminating a specific process when it starts up.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Cause PowerShell to Wait for Specific Event

$
0
0

Summary: Cause Windows PowerShell to wait for a specific event to be generated.

Hey, Scripting Guy! Question How can I make my Windows PowerShell console halt until an event that I registered by using the 
           Register-CimIndicationEvent cmdlet occurs?

Hey, Scripting Guy! Answer Use the Wait-Event cmdlet and specifiy the source identifier. For example, if you registered
           for an event named nq, use the following command:

Wait-Event -SourceIdentifier nq

Use PowerShell to Repeatedly Terminate Specific Processes

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to terminate processes that start at random times.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a question that I hope you can answer. It seems that there are several processes that keep starting on my computer running Windows 8.1. That eat up lots of resources, and I cannot seem to find where they are coming from. I have used the Sysinternals autoruns command, and yet they keep coming and consuming. What I would really love is to have a script that detects when these bogus processes start, and then terminate them. Can you help with that?

—CF

Hey, Scripting Guy! Answer Hello CF,

Microsoft Scripting Guy, Ed Wilson, is here. This morning it is Friday. WooHoo, the weekend baby! Actually, my weekends are pretty much like my weekdays, so I am not really one of those people who lives for the weekend. Having said that, it is still nice to have a weekend once in a while.

So I am sitting on the porch, sipping a cup of English Breakfast tea with a cinnamon stick and a bit of lemon grass in it. I am checking my email at scripter@microsoft.com, and I ran across your email. The answer is, "Yes, I can write a bit of Windows PowerShell script that will detect when a specific process starts, and once that process starts, I can terminate that specific process." The technique is amazingly simple with Windows PowerShell. (Using VBScript in the old days, this was a bit of a bear.)

Detecting a process and terminating it

There are three steps required to detect when a specific process starts and then terminate the process:

  1. Write a WQL query that uses the Win32_ProcessStartTrace WMI class with a filter for a specific process.
  2. Register to receive events by using the Register-CimIndicationEvent Windows PowerShell cmdlet. In the command, use the query and specify an action to stop the process.
  3. Use the Wait-Event cmdlet to wait until an event triggers the action.

   Note  Today's blog post builds on concepts that I discussed yesterday in Use PowerShell to Monitor Specific Process Creation, so you should review that post first.
   To work properly, the Windows PowerShell console must launch with Admin rights. Right-click the Windows PowerShell console icon, and select “Run As Administrator.”

Develop the query

To develop my query, I use WMI Query Language (WQL) and I query the Win32_ProcessStartTrace WMI class. This class is designed especially to detect when a new process begins, and therefore, it is easy to use. I use the Where clause to specify the ProcessName I want to detect. This requires that I specify the program file extension (unlike Get-Process, which only uses the base name). Here is the query I use:

$Q = "Select * from win32_ProcessStartTrace where processname = 'notepad.exe'"

Register for events

Now I want to register for events. In addition, I want to specify an action—that is, I want to stop the process when it launches. To do this, I will use an automatic variable, $event, that is generated when an event occurs. I use the $event variable to drill into the object. I go into SourceEventArgs, grab the NewEvent property and select the ProcessID. This is the program PID, and I pass it to the Stop-Process cmdlet. When a new instance of Notepad launches, within a short period of time, it is terminated. Here is the script:

Register-CimIndicationEvent -Query $q -SourceIdentifier Q -Action {Stop-Process $event.SourceEventArgs.newevent.processID}

Wait for it…

Now I need to wait for an event to arise. To do this, I use the Wait-Event cmdlet. This cmdlet pauses the Windows PowerShell console and waits for events to arise. When I no longer want this to happen, I use Ctrl-C to break out of the wait cycle. Nothing appears on the screen while it is waiting or when a process terminates. The Windows PowerShell console is shown here:

Image of command output

When I break the Wait-Event cycle, Windows PowerShell no longer terminates the process. But if I start up again, Wait-Event (prior to removing the subscription) will continue to work. When I close Windows PowerShell, everything is reset to defaults.

CF, that is all there is to using Windows PowerShell to terminate processes that keep starting. Join me tomorrow when we will have another great guest blog post by Windows PowerShell MVP, Sean Kearney.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Display Message in 16 Colors

$
0
0

Summary: Use Windows PowerShell to display a message in 16 colors.

Hey, Scripting Guy! Question How can I use Windows PowerShell to easily display a message in 16 different colors to start my presentation
           with something a little different?

Hey, Scripting Guy! Answer Use the range operator to create 16 numbers. Pipe the numbers to the Foreach-Object cmdlet. In the
           script block, use Write-Host, and specify the foreground color as the number.
           Here is the command (using aliases for conciseness):

0..15 | %{write-host -f $_ 'hi'}

Weekend Scripter: Playing with PowerShell Processes and Events

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about playing with processes and events in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. This morning I woke up and I had an idea. This happens to me sometimes. Not often. But occasionally. And here it was. I could not get it out of my mind...

Can I use an event from a specific process? I wonder. I should be able to do this, but hmmmm. I know a process that I retrieve by using the Get-Process cmdlet is really an instance of System.Diagnostics.Process. I also know that in addition to having methods and properties, it has events. Here are the events I get when I look at a specific process:

PS C:\> Get-Process notepad | Get-Member -MemberType Event

   TypeName: System.Diagnostics.Process

Name               MemberType Definition

----               ---------- ----------

Disposed           Event      System.EventHandler Disposed(System.Object, System.Event...

ErrorDataReceived  Event      System.Diagnostics.DataReceivedEventHandler ErrorDataRec...

Exited             Event      System.EventHandler Exited(System.Object, System.EventArgs)

OutputDataReceived Event      System.Diagnostics.DataReceivedEventHandler OutputDataRe...

Without getting all froggy and looking stuff up in MSDN, I figure that I have a pretty good idea of what an Exitedevent might really be—it is an event that is raised when a particular process exits.

Let me try the Windows PowerShell console

I decided to launch Notepad, and then use the Get-Process cmdlet to retrieve that instance of Notepad. Now I use the Register-ObjectEvent cmdlet, supply the Process object as an InputObject, and I specify that I want to monitor for the Exited event. In the action portion, I start Notepad, and I unregister the event subscriber.

I press ENTER, and I wait.

I close Notepad…

And I wait.

And I wait.

And I wait.

Nothing happens. Bummer.

I press ENTER in the Windows PowerShell console, and suddenly Notepad reappears. Hmmm…I wonder if this is an STA/MTA kind of thing. I launch Windows PowerShell in MTA mode (powershell –mta) and in STA mode (powershell –sta). Still nothing. It only works after I press ENTER. Bummer. Not such a good deal at this point. By the way, here is a screenshot:

Image of command output

One of the nice things about using the Register-ObjectEvent cmdlet is that it does not require me to launch an elevated Windows PowerShell console (or ISE). This is great because I seldom launch Windows PowerShell with elevated permission. Most of the time, I try things without elevation.

What about the ISE?

I open the Windows PowerShell ISE and type my script:

Start-Process notepad

$n = Get-Process notepad

$job = Register-ObjectEvent -InputObject $n -EventName exited -SourceIdentifier notepad -Action {

    Start-Process notepad

    Get-EventSubscriber | Unregister-Event }

When I run the script, Notepad appears. Cool. I close Notepad, and it immediately reappears. Cool. If I close that instance of Notepad, however, Notepad remains closed. This is due to the fact that I am monitoring a specific instance of Notepad, and once I close that particular instance of Notepad, the new instance is a different process. Therefore, I cannot call the object event from that new process because I have not captured it in the $n variable.

Advantages?

Using Register-ObjectEvent seems to have the following advantages for me:

  • Does not automatically require admin rights
  • Syntax is easier
  • Really fast
  • Lower overhead

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to Find Object Events

$
0
0

Summary: Find events from objects by using Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to find what events are offered by a process?

Hey, Scripting Guy! Answer Use Get-Process to retrieve an instance of the process, pipe the results to Get-Member,
           and specify a MemberType Event:

notepad

Get-Process notepad | Get-Member -MemberType Event

Weekend Scripter: Use PowerShell to Connect to GoToMeeting

$
0
0

Summary: Use Windows PowerShell to provide a GUI input and build a custom URL for Internet Explorer.

Honorary Scripting Guy, Sean Kearney, here to share a little fun that I have enjoyed on the client site. I say it was fun because, to be quite honest, the problem really wasn't that challenging, but to make the solution seamless to an end user was.

Our challenge is to try to get a computer to connect to GoToMeeting without installing the client software.

The problem was that the application would try to force a user through a complete download of the client application, then attempt an installation. Only after this process would you see a friendly window indicating, "Hey, you could click here to run a version that totally doesn't need local Admin rights, dude!"

To be honest, it didn't say that, but it did take a while before the screen prompted me with an alternate method of connecting.

What it finally presented was a URL that allowed a manual method of connecting to the system with a Flash-based version of the software. The URL looked something like this:

https://www1.gotomeeting.com/join/123123123?clientType=flash

This would launch a GoToMeeting with the ID # of 123123123.

The answer was really quite simple:

  • Prompt the user for the meeting ID number.
  • Build the URL with the meeting ID included.
  • Launch Internet Explorer with the supplied URL.

To prompt for the meeting ID number, we could get away with using Read-Host:

$MeetingID=READ-HOST 'Enter GotoMeeting ID #'

But I wanted this to be usable by normal users. I wanted a simple GUI popup for this.

With a little digging on the Internet, I found a simple routine to provide a popup box by using Visual Basic:

[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null

$MeetingID = [Microsoft.VisualBasic.Interaction]::InputBox("GotoMeeting Flash", "GotoMeeting ID")

This would provide a simple text box on the screen that looks like this:

Image of text box

However, in some cases, the meeting ID for GoToMeeting is presented in email in the following format:

000-000-000

To ensure that users could copy and paste the number into the meeting box, we can leverage a method to replace any hyphens ( - ) found with nothing:

$MeetingID=$MeetingID.Replace("-","")

Now that we have the meeting ID, all we need to do is build the URL:

$Url="https://www1.gotomeeting.com/join/"+$MeetingID+"?clientType=flash"

At this point, we can launch a web browser with the URL. We create a connection to Internet Explorer first with New-Object:

$ie=new-object -ComObject internetexplorer.application

Now that we have the connection, we pass the URL that we created to Internet Explorer for navigation:

$ie.Navigate2($url)

Afterwards, we make the browser visible:

$ie.Visible=$TRUE

However, there's a small issue. What if the user was to cancel out of the input window? We don't want the URL opening a browser with blank data. We can alleviate this with a simple if statement to allow only for input with content:

If ($MeetingID) {

}

Our final script will look like this:

# Present Popup Box

[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null

$MeetingID = [Microsoft.VisualBasic.Interaction]::InputBox("GotoMeeting Flash", "GotoMeeting ID")

# Remove all hyphens from the inputted data

$MeetingID=$MeetingID.Replace("-","")

# If we get something input into the MeetingID go ahead

If ($MeetingID) {

# Build the URL

$Url="https://www1.gotomeeting.com/join/"+$MeetingID+"?clientType=flash"

# Open connection to Internet Explorer

$ie=new-object -ComObject internetexplorer.application

# Navigate to the URL in question

$ie.Navigate2($url)

# Make the Browser Visible

$ie.Visible=$TRUE

}

With all of this in place, we can launch a script that will produce a simple popup box that users can paste the MeetingID into.

The fun part is that with this configuration, you now have a solution to build URLs programmatically. You might even be able to leverage a similar solution for Lync Meetings to launch the Silverlight client by default.

How can you leverage this script? The possibilities are all up to you!

We now return you to your regularly scheduled weekend...

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send an email to the Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, remember, the Power of Shell is in You.

Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy


PowerTip: Remove Leading and Trailing Spaces with PowerShell

$
0
0

Summary: Use the Trim() method to remove extraneous space from a String object.

Hey, Scripting Guy! Question Is there an easy way to drop all spaces that are before or after a string in Windows PowerShell?

Hey, Scripting Guy! Answer Use the Trim() method to remove all spaces before and after a string from the output, for example:

$Something='                 I love PowerShell                     '

$Something.trim()

Fun Formatting Ones—Part 1: The Task

$
0
0

Summary: June Blender explains Doug Finke's multiplication and formatting trick. Today...the task.

Microsoft Scripting Guy, Ed Wilson, is here. Today, Honorary Scripting Guy, June Blender, examines a multiplication and formatting trick that Doug Finke posted on the PowerShell Facebook page. I've broken this article into two parts:

Part 1: The Task  Explains what Doug was trying to do and how to do it in Windows PowerShell.

Part 2: The Method  Explains the techniques that Doug used.

If you already understand The Task, you can wait for Part 2:The Method. Now, here's June...

Windows PowerShell MVP, Doug Finke, author of Windows PowerShell for Developers, is one of nicest and most clever people in the Windows PowerShell community. I'm a big fan, because I almost always learn something really new when I read Doug's work. You can contact Doug at:

Last week, Doug posted the following fun multiplication and formatting trick:

Image of command output

Many people "liked" the post (including me), but I realized that not everyone understood why it worked. If you do understand it, you might be able to use the techniques for slightly less fun things, like work. Let's tease it apart and learn all that Doug has to teach. This post explains what Doug was trying to do and how to do it in Windows PowerShell.

It looks like Doug had a bit of fun with the cool patterns of squares of numbers with 1s, and he wanted to create this table of products in Windows PowerShell.

1    x 1    = 1

11   x 1    = 11

11   x 11   = 121

111  x 111  = 12321

1111 x 1111 = 1234321

Each row of the table multiplies n digits of 1s and shows the product. The number of 1-digits increases with each row. To do this, he creates a loop that goes from 1 to 8 and prints that number of 1s and their product. It would look like:

  • Row 1: Print one 1. Then print the product of 1 x 1.
  • Row 2: Print two 1s. Then print the product of 11 x 11.
  • Row 8: Print eight 1s. Then print the product of 11111111 x 11111111.

To do this, you use a loop that starts at 1 and ends at your stopping point, which for Doug, is 8. This is a bit tricky, because you're working with strings, not numbers.

The essential part of this task is to print a specified number of 1s (twice). To do that, you use string multiplication, so let's start with that topic.

Multiplying strings

Most scripting and programming languages have operators that add and multiply numbers, for example:

PS C:\> 3 + 4

7 <yawn>

 

PS C:\> 3 * 4

12 <duh>

But Windows PowerShell lets you use the same + and * operators to add and multiply strings.

When you add strings, Windows PowerShell concatenates them (no spaces):

PS C:\> "Power" + "Shell"

PowerShell

When you multiply a string by a number n, Windows PowerShell copies the string n times and concatenates the results:

PS C:\> "Car" * 3

CarCarCar

String multiplication isn't commutative. Because the first operand determines the operation, the string must come first in a string multiplication expression. If an integer comes first, Windows PowerShell tries to do integer multiplication, which doesn't permit strings.

PS C:\> 3 * "Car"

Cannot convert value "Car" to type "System.Int32". Error: "Input string was not in a correct format."

At line:1 char:1

+ 3 * "Car"

+ ~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException

    + FullyQualifiedErrorId : InvalidCastFromStringToInteger

String multiplication lets you do some cool things. This For loops starts with $i = 1 and counts up to $i = 5. In each pass, it multiplies the "x" string by the value $i:

PS C:\> for ($i = 1; $i -le 5; $i++) {"x" * $i}

x

xx

xxx

xxxx

xxxxx

This is a handy trick. In languages that don't allow string multiplication, like Python and Java, you need nested loops to do this.

Doug has fun with the string "1". Note that "1" is a string enclosed in quotation marks, not an integer. In Doug's statement, he multiplies the "1" string by $i where i goes from 1 to 8:

for ($i = 1; $i -le 8; $i++)

{

"1" * $i

}

 

1

11

111

1111

11111

111111

1111111

11111111

This creates the first set of 1s for Doug's display. But he needs two sets of 1s on each row, separated by an " x ". We'll use string addition to get it.

Adding Strings

To get the first part of Doug's display, we can use a loop that multiplies a number by a "1" string . But Doug's table requires two sets of 1s, separated by an " x ".

To add the " x ", we'll use the addition operator (+). It concatenates the first set of ones, the " x " string, and the second set of ones.

("1" * $i) +" x " + ("1" * $i)

When $i is 1, it produces the first row. When $i is 2, it produces the second row:

PS C:\ps-test> $i = 1

PS C:\ps-test> ("1" * $i) + " x " + ("1" * $i)

1 x 1

 

PS C:\ps-test> $i = 2

PS C:\ps-test> ("1" * $i) + " x " + ("1" * $i)

11 x 11

Now, let's use a loop that starts $i at 1 and goes to 8 (inclusive):

for ($i = 1; $i -le 8; $i++)

{

    "1" * $i + " x " + "1" * $i

}

1 x 1

11 x 11

111 x 111

1111 x 1111

11111 x 11111

111111 x 111111

1111111 x 1111111

11111111 x 11111111

Looking good!

The next part is" = " string, so let's add it:

for ($i = 1; $i -le 8; $i++)

{

    "1" * $i + " x " + "1" * $i  + " = "                      

}

 

1 x 1 =

11 x 11 =

111 x 111 =

1111 x 1111 =

11111 x 11111 =

111111 x 111111 =

1111111 x 1111111 =

11111111 x 11111111 =

The "1" * $i part of the equation is getting a bit repetitive (and error prone). Let's assign it to the $n variable. Then, we can replace the instances of "1" * $i with $n:

for ($i = 1; $i -le 8; $i++)

{

    $n = "1" * $i   #Assignment

   

    $n + " x " + $n  + " = "                       

}

1 x 1 =

11 x 11 =

111 x 111 =

1111 x 1111 =

11111 x 11111 =

111111 x 111111 =

1111111 x 1111111 =

11111111 x 11111111 =

Next, Doug wants to display the product of each equation. To get the product, he needs to multiply numbers, not strings. Let's see how he does it.

Invoke-Expression: Convert number strings to numbers

Doug wants to display the products of the numbers that he's printing:

11 * 1 = 11

11 * 11 = 121

111 * 111 = 12321

1111 * 1111 = 1234321

But, he was playing with strings of 1s, not the number 1. To convert the strings of numbers to numbers for the expression, he uses the Invoke-Expression cmdlet. Let's see how this works:

PS C:\> "2 * 3"    #This is a string.

"2 * 3"

 

PS C:\> Invoke-Expression -Command "2 * 3"  #This is a string.

PS C:\> 6

Now, let's try it with "1" strings:

PS C:\> "111 * 111"    #This is a string.

"111 * 111"

 

PS C:\> Invoke-Expression -Command "111 * 111"  #Still a string.

PS C:\> 12321

Be careful. To make this work, the value must be a single string, and all values and operators must be convertible to an arithmetic expression.

You can also pipe the number string to the Invoke-Expression cmdlet, which is what Doug did. And he used the iex alias of Invoke-Expression. The next three statements are equivalent, and they can be used interchangeably:

PS C:\> Invoke-Expression -Command "111 * 111"

PS C:\> 12321

 

PS C:\> "111 * 111" | Invoke-Expression

PS C:\> 12321

 

PS C:\> "111 * 111" | iex

PS C:\> 12321

This Invoke-Expression feature gives Doug the product for his "ones x ones = product" statement. Let's add the statement to the For loop block:

for ($i = 1; $i -le 8; $i++)

{

    $n = "1" * $i

   

    $n + " x " + $n  + " = " +

 

    ("$n * $n" | iex)

}

 

1 x 1 = 1

11 x 11 = 121

111 x 111 = 12321

1111 x 1111 = 1234321

11111 x 11111 = 123454321

111111 x 111111 = 12345654321

1111111 x 1111111 = 1234567654321

11111111 x 11111111 = 123456787654321

This completes the task. It's tricky, but it works. In tomorrow's post, we'll talk about the techniques that Doug uses, including an interesting loop and pretty formatting. Stay tuned!

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

June Blender, Honorary Scripting Guy

PowerTip: Display Hidden Properties from Object

$
0
0

Summary: Learn how to use Windows PowerShell to display hidden properties from an object.

Hey, Scripting Guy! Question How can I see if there are any hidden properties on an object I have returned from Windows PowerShell?

Hey, Scripting Guy! Answer Use the -Force parameter with either Format-List or Format-Table, for example:

Get-ChildItem C: | Format-List * -Force

  

Fun Formatting Ones—Part 2: The Method

$
0
0

Summary: June Blender explains Doug Finke's multiplication and formatting trick. Today...the method.

Microsoft Scripting Guy, Ed Wilson, is here. This is the second part of a two-part series written by June Blender, Honorary Scripting Guy. In the first part, Fun Formatting Ones—Part 1: The Task, we discussed the task of printing Doug's multiplication table. In this part, we'll talk about the techniques that he used, including formatting the table.

Note If you already understand an element of the method, such as Ranges, you can skip that section. The sections are independent.

Here's June...

Today, I'll explain the techniques that Doug used to solve the task, including an interesting loop and string formatting.

Recap of Part 1

Let's recap what we learned in Part 1.

Doug wanted to create this table of products in Windows PowerShell.

       1 *        1 = 1

      11 *       11 = 121

     111 *      111 = 12321

    1111 *     1111 = 1234321

   11111 *    11111 = 123454321

  111111 *   111111 = 12345654321

 1111111 *  1111111 = 1234567654321

11111111 * 11111111 = 123456787654321

Each row of the table multiplies n digits of 1s and shows the product. The number of 1-digits increases with each row. To do this, he creates a loop that goes from 1 to 8 and prints that number of 1s and their product. For each row:

  • Print n digits of "1"   ("1" * $i)
  • An " x "
  • Repeat n digits of "1"
  • An " = "
  • The product of the n digits, as though they were numbers

To get the product of a number string, use the Invoke-Expression cmdlet. It evaluates the string as an arithmetic equation, for example:

PS C:\> "1111 * 1111"

"1111 * 1111"

 

PS C:\> Invoke-Expression -command "1111 * 1111"

1234321

Doug uses a more succinct form of the same Invoke-Expression command:

"1111 * 1111" | iex

So the Windows PowerShell statement that creates the string for each row is:

"1" * $i + " x " + "1" * $i + " = " = ("1" * $i) * ("1" * $i) | iex

We assign "1" * $i to the variable $n to make this a bit cleaner:

$n = "1" * $i

$n + " x " + $n + " = " + ("$n * $n" | iex)

In a loop from 1 to 8 (inclusive):

for ($i = 1; $i -le 8; $i++)

{

$n = "1" * $i

 

$n + " x " + $n + " = " + ("$n * $n" | iex)

}

1 x 1 = 1

11 x 11 = 121

111 x 111 = 12321

1111 x 1111 = 1234321

11111 x 11111 = 123454321

111111 x 111111 = 12345654321

1111111 x 1111111 = 1234567654321

11111111 x 11111111 = 123456787654321

That's really close. But in his post, Doug uses a different type of loop and string formatting. Let's discuss those.

Ranges

Instead of using a typical For loop, Doug pipes a range of numbers, 1-8, to the ForEach-Object cmdlet.

1..8 | ForEach-Object { 

$n = "1" * $i; $n + " x " + $n + " = " + ("$n * $n" | iex)

}

A range is an array of numbers that starts at a beginning number, stops at (and includes) an ending number, and includes all numbers in between in numeric order. For example, a range of numbers from 1 to 5 is:  1, 2, 3, 4, 5. The syntax for a range is: 

$start..$stop

There are no intervening spaces. You can use negative and positive numbers, and you can create ranges that count down or up, for example:

PS C:\> 0..6

0

1

2

3

4

5

6

 

PS C:\> -4..4

-4

-3

-2

-1

0

1

2

3

4

PS C:\> 8..-3

8

7

6

5

4

3

2

1

0

-1

-2

-3

Best of all, the starting and the stopping numbers can be variables or expressions that can change over time.

PS C:\> $start = 0

PS C:\> $stop = 5

PS C:\> $start..$stop

0

1

2

3

4

5

PS C:\> $start = 6

PS C:\> $start++..$stop--

7

6

Piping the items in the range

Doug creates a range of numbers from 1 to 8 (1..8) and then pipes the range to the ForEach-Object cmdlet.

When you send it a collection of objects (any kind), the pipeline operator ( | ) takes each item from the collection (one at a time) and sends it to the next command:

1..8 | <next thing>

Is equivalent to:

1, 2, 3, 4, 5, 6, 7, 8 | <next thing>

Or:

1| <next thing>

2| <next thing>

8| <next thing>

In the pipeline command, the current object coming down the pipeline is represented by good old $_, for example:

PS C:\> 1..8 | ForEach-Object {"I need $_ new servers."}

I need 1 new servers.

I need 2 new servers.

I need 3 new servers.

I need 4 new servers.

I need 5 new servers.

I need 6 new servers.

I need 7 new servers.

I need 8 new servers.

You can use a range controlled ForEach-Object command in place of any For loop. Here's the equivalent For loop. The $i variable starts at 1, continues while $i is less-than or equal-to 8 (because 8 is included in the range), and it increments $i on each pass. In this case, the value that is increasing is $i, not $_:

PS C:\> For ($i = 1; $i -le 8; $i++) {"I need $i new servers."}

I need 1 new servers.

I need 2 new servers.

I need 3 new servers.

I need 4 new servers.

I need 5 new servers.

I need 6 new servers.

I need 7 new servers.

I need 8 new servers.

Let's replace the For loop that creates Doug's 1s table with a statement that pipes a range to a ForEach-Object command. Here's the For loop:

for ($i = 1; $i -le 8; $i++)

{

    $n = "1" * $i

   

    $n + " x " + $n  + " = " + ("$n * $n" | iex)

}

Here's the new range controlled ForEach loop. The $_ takes the place of $i in the For loop:

1..8 | ForEach {

    $n = "1" * $_

    $n + " x " + $n  + " = " + ("$n * $n" | iex)

}

Doug makes it a one-liner. In a one-liner, we don't have each command on a separate line, so we add a statement terminator ( ; ) to end the assignment statement. The remainder of the command is a single statement:

1..8 | ForEach {$n = "1" * $_; $n + " x " + $n  + " = " + ("$n * $n" | iex)}

 

1 x 1 = 1

11 x 11 = 121

111 x 111 = 12321

1111 x 1111 = 1234321

11111 x 11111 = 123454321

111111 x 111111 = 12345654321

1111111 x 1111111 = 1234567654321

11111111 x 11111111 = 123456787654321

Hanging in there? Now, let's add some formatting.

Formatting strings with -f

Windows PowerShell uses the formatting statements in the .NET library that are designed for strings. They're really powerful, so it's worth learning how to use them.

The primary MSDN doc about this topic starts with Composite Formatting, a really well-written topic that is worth reading and bookmarking. There are lots of blog posts about string formatting in Windows PowerShell, including one by Ed Wilson (Use PowerShell to Format Strings with Composite Formatting) and one by me (String Formatting in Windows PowerShell).

String formatting is powerful and complex, but we can start with the basics. A formatted string statement has the following form:

"{0} and {1}" -f <expressionA>, <expressionB>

The -f (for Format) divides the statement into two parts.

The double-quoted string to the left of the -f includes integers that are placeholders for strings. The placeholder integers must begin with 0 and increase by 1. They're enclosed in curly braces, for example:

            {0}

To the right of the -f is a comma-separated array of one or more expressions. The result of each expression takes the place of a placeholder. The first expression takes the place of {0} in the string; the second expression takes the place of {1}, and so on, for example:

PS C:\> "{0} is {1}." -f "PowerShell", "fun"

PowerShell is fun. 

 

PS C:\>"{0} is {1}" -f ("car" * 3), "redun" + "dant"    

carcarcar is redundant.

The placeholders can be repeated in the string, but you need to have exactly one expression for each placeholder.

PS C:\>"How much {0} could a {0}{1} {1} if a {0}{1} could {1} {0}?" -f "wood", "chuck"

How much wood could a woodchuck chuck if a woodchuck could chuck wood?

Let's see how Doug used formatting. He started with a string of the form: 

ones x ones = product

$n x $n = ("$n * $n" | iex)

He replaced the ones with "{0}" and product with "{1}". Note that this is a single string. We don't need to use string addition to concatenate the x and the =. They're just part of the string:

"{0} x {0} = {1}"

Then he used the -f statement to replace every {0} with $n and every {1} with ("$n * $n | iex"):

"{0} x {0} = {1}" -f $n, ("$n * $n | iex")

So, the original statement:

1..8 | ForEach {$n = "1" * $_; $n + " x " + $n  + " = " + ("$n * $n" | iex)}

Becomes:

1..8 | ForEach {$n = "1" * $_; "{0} x {0} = {1}" -f $n, ("$n * $n" | iex)}
The result is the same:

1 x 1 = 1

11 x 11 = 121

111 x 111 = 12321

1111 x 1111 = 1234321

11111 x 11111 = 123454321

111111 x 111111 = 12345654321

1111111 x 1111111 = 1234567654321

11111111 x 11111111 = 123456787654321

The last step is aligning it so it looks even cooler.

Alignment in formatted strings

Among the many things you can do with string formatting is to align the characters in a string. To place characters in a string of a certain size, add an alignment value to the placeholder. Positive values align to the right, and negative values align to the left. The syntax is:

            {<placeholder>,<alignment>}

For example, this statement declares placeholder {0} and places the result in a 5-character string where the "a" character is in position 5:

PS C:\> "{0, 5}" -f "a"

    a

This statement uses a negative value to left-align the "a" in a 5-character space. I've put an "x" in the next position, so you can see the alignment more clearly:

PS C:\ps-test> "{0, -5}{1}" -f "a", "x"

a    x

In his "Fun with Ones" post, Doug uses an alignment value of 9 (+9) to right-align the $n strings in a 9-character field. The alignment changes this:

1..8 | ForEach {$n = "1" * $_; "{0} x {0} = {1}" -f $n, ("$n * $n" | iex)}

1 x 1 = 1

11 x 11 = 121

111 x 111 = 12321

1111 x 1111 = 1234321

11111 x 11111 = 123454321

111111 x 111111 = 12345654321

1111111 x 1111111 = 1234567654321

11111111 x 11111111 = 123456787654321

To this:

1..8 | ForEach {$n = "1" * $_; "{0, 9} x {0, 9} = {1}" -f $n, ("$n * $n" | iex)}

        1 x         1 = 1

       11 x        11 = 121

      111 x       111 = 12321

     1111 x      1111 = 1234321

    11111 x     11111 = 123454321

   111111 x    111111 = 12345654321

  1111111 x   1111111 = 1234567654321

 11111111 x  11111111 = 123456787654321

So, what did we learn from taking some time to interpret Doug's work? Here we go...

  • Ranges

PS C:\>1..8

1

2

3

4

5

6

7

8

  • Replacing a For loop with a range piped to a ForEach-Object command

for ($i = 1; $i -le 8; $i++) {"1" * $i}

1

11

111

1111

11111

111111

1111111

11111111

1..8 | ForEach-Object {"1" * $_}

1

11

111

1111

11111

111111

1111111

11111111

  • Multiplying strings

PS C>"1" * 3

111

  • Adding strings

PS C:\> ("1" * 3) + " x " + ("1" * 3) + " = "

111 x 111 =

  • Using Invoke-Expression to evaluate number strings

PS C:\ps-test> "111 * 111"

111 * 111

PS C:\ps-test> Invoke-Expression -Command "111 * 111"

12321

 

PS C:\ps-test> "111 * 111" | Invoke-Expression

12321

 

PS C:\ps-test> "111 * 111" | iex

12321

  • Formatting strings 

PS C:\> $n = "111"

PS C:\> $n + " + " + $n

111 + 111

 

PS C:\> "{0} + {0}" -f $n

111 + 111

  • Aligning formatted strings

PS C:\> "{0} + {0}" -f $n

111 + 111

 

PS C:\> "{0, 5} + {0, 5}" -f $n

  111 +   111

Not bad lessons for quick Facebook post. Thanks, Doug!

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

June Blender, Honorary Scripting Guy

PowerTip: Read First Line of File with PowerShell

$
0
0

Summary: Learn how to read only the first line of a file by using Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to read only the first line of a file?

Hey, Scripting Guy! Answer Introduced in Windows PowerShell 3.0, you can use the -First parameter, for example:

Get-Content C:\fso\batteryReport.txt -First 1

Viewing all 3333 articles
Browse latest View live




Latest Images