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!
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!
…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