Hey, Scripting Guy! How can I use Windows PowerShell to Search a Text File for multiple strings?

Hey, Scripting Guy! How can I use Windows PowerShell to Search a Text File for multiple strings?

  • Comments 1
  • Likes

 

Bookmark and Share

 

Q: Hey Scripting Guy! I need help migrating a script from VBScript to Windows PowerShell 2.0. The VBScript is one that I based upon an old Hey Scripting Guy! article. I use it to search a text file for specific words. If the words are found, the script displays a message stating this fact. I do not want to learn Windows PowerShell right now, but I would like to have a Windows PowerShell script that basically looks like VBScript so I can understand it. Is this possible, or do I need to spend a couple of weeks learning Windows PowerShell first?

-- SA

A: Hello SA, Microsoft Scripting Guy Ed Wilson here, the Scripting Wife and I got back from vacation early Sunday morning, or late Saturday night depending on your perspective. I had spent the week taking a woodworking class at a school in the Smokey Mountains. Spending a week using hand tools to build a piece of fine furniture is relaxing to me. I also got to see my old high school friend, a cousin, nephew and my brother … so it was a great week. While I was in class, the Scripting Wife spent the time running around with relatives, reading and mellowing out by the lake. I spent my days in class working on a dovetailed blanket chest, and my evenings working on a really cool Windows PowerShell module.

SA, I found the VBScript you were talking about in the How Can I Search for Two Items in a Text File Hey Scripting Guy! article from August 1, 2005. The SearchFileForTwoItems.vbs script is seen here.

SearchFileForTwoItems.vbs

Const ForReading = 1
blnFound = False
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\fso\Hsg62910.txt", ForReading)
strContents = objFile.ReadAll
objFile.Close
If InStr(strContents, "Windows 2000") Then
blnFound = True
End If
If InStr(strContents, "Windows XP") Then
blnFound = True
End If
If blnFound Then
Wscript.Echo "Either Windows 2000 or Windows XP appears in this file."
Else
Wscript.Echo "Neither Windows 2000 nor Windows XP appears in this file."
End If

The SearchFileForTwoItems.vbs script can be directly translated into Windows PowerShell 2.0. The translated SearchFileForTwoItemsTransFmVBS.ps1 script is seen here.

SearchFileForTwoItemsTransFmVBS.ps1

add-type -AssemblyName microsoft.visualbasic
$strings = "microsoft.visualbasic.strings" -as [type]
$VbCrLf = "`r`n"
$forReading = 1
$blnFound = $false
$objFSO = New-Object -ComObject scripting.filesystemobject
$objFile = $objFSO.OpenTextFile("C:\fso\hsg62910.txt", $forReading)
$strContents = $objfile.ReadAll()
$objFile.close()
if($strings::instr($strContents, "Windows 2000"))
{$blnFound = $true}
if($strings::instr($strContents, "Windows XP"))
{$blnFound = $true}
If($blnFound)
{"Either Windows 2000 or Windows XP appears in this file"}
Else
{"Neither Windows 2000 or Windows XP appears in this file"}

The “secret” to gaining access to the VBScript instr function is to load the Microsoft.VisualBasic .NET Framework assembly. In Windows PowerShell 2.0 this is done by using the Add-Type cmdlet as seen here.

add-type -AssemblyName microsoft.visualbasic

Once the Microsoft.VisualBasic assembly has been added into the current Windows PowerShell session, the members of the Microsoft.VisualBasic.Strings .NET Framework class can be accessed directly. To use the instr static method, you would use syntax that is similar to that seen here.

PS C:\Users\ed.NWTRADERS> [microsoft.visualbasic.strings]::instr("a","abc")
0

The problem with this is that it is an awful lot of typing, therefore, I prefer to create an alias for the particular .NET Framework class I wish to use. To do this, use the –as operator and cast the string as a type. This is shown here.

$strings = "microsoft.visualbasic.strings" -as [type]

By creating an alias for the class, I can use a more intuitive syntax and avoid typing square brackets and the .NET Framework namespace name. The revised syntax is seen here.

PS C:\Users\ed.NWTRADERS> $strings::instr("abc","a")

1

To create a carriage return and a line feed Windows PowerShell uses a backtick r and a backtick n. When placed together they are “`r`n” --a less than intuitive syntax. To make the script easier to read, I create a variable $vbcrlf that will be familiar to VBScript users. This is seen here.

$VbCrLf = "`r`n"

When using the filesystemobject to open a text file, the default method is to open the file for reading, it is a common practice to create a variable or a constant called forreading to hold this instruction. This is seen here.

$forReading = 1

One “improvement” to the script, is to assign a default value of $false to the $blnFound variable. It is always a best practice to initialize counter and control type variables to ensure predictable behavior from the script. This is seen here.

$blnFound = $false

The next thing the VBScript does is create the filesystemobject and store it in the objfso variable. It then opens the text file, reads the entire contents of the file into a variable and closes the file. These are the exact same steps that are performed in the section of the script seen here. To create a new object use the New-Object cmdlet and specify the –comobject parameter. One thing that is nice about the new-object cmdlet is that it knows that you are supplying a string to it for the object name, and therefore you do not need to use quotation marks around the string. The returned filesystemobject is stored in the $objFSO variable –which is the same name that was used in the original VBScript. The openTextFile method is exactly the same as the one used in the VBScript. When using Windows PowerShell, all methods must use parentheses –even if they do not accept any parameters. This section of the script is seen here.

$objFSO = New-Object -ComObject scripting.filesystemobject
$objFile = $objFSO.OpenTextFile("C:\fso\hsg62910.txt", $forReading)
$strContents = $objfile.ReadAll()
$objFile.close()

To determine if a string exists in the text from the file, the VBScript used the instr function. The instr static method from the Microsoft.visualbasic.strings .NET Framework class corresponds to the instr function from VBScript. The instr method returns a number that indicates the position in which the string was located. In the VBScript, the location where the string was found does not matter. What matters is a value that is greater than 0. If a string is not found, instr returns 0. This is seen here.

PS C:\Users\ed.NWTRADERS> $strings::instr("abc","e")
0

Therefore if instr finds the string, the if statement returns true, and therefore the $blnFound variable is assigned the value of $true. This section of the script works the same as the original VBScript and is shown here.

if($strings::instr($strContents, "Windows 2000"))
{$blnFound = $true}
if($strings::instr($strContents, "Windows XP"))
{$blnFound = $true}

If neither string is found, the Else statement kicks in and will display a string that says that the values were not found in the file. This is shown here.

Else
{"Neither Windows 2000 or Windows XP appears in this file"}

SA, as promised I have migrated your VBScript to Windows PowerShell 2.0 and maintained the original syntax. I should tell you however, that you can accomplish essentially the same thing by using the Select-String cmdlet. The Select-String cmdlet accepts a path to a file, and it will search the file for a string that you specify. In addition, it will display the sentence where the text string was located. This is shown here.

PS C:\ed> Select-String -Path C:\fso\HSG62910.txt -Pattern "Windows 2000", "Windows XP"
C:\fso\HSG62910.txt:2:One of these sentences contains the string Windows 2000.
C:\fso\HSG62910.txt:3:The other sentence contains the string Windows XP.

It is possible to completely replicate the functionality of the SearchFileForTwoItems.vbs script in a single line, but it would be hard to read. Therefore, I decided to create a script … note that I used the same code from the SearchFileForTwoItemsTransFmVBS.ps1 script as seen here.

{"Either Windows 2000 or Windows XP appears in this file"}
Else
{"Neither Windows 2000 or Windows XP appears in this file"}

I was able to use the same code, because Windows PowerShell allows you to treat the output of the Select-String cmdlet as a Boolean value. The complete SearchFileForTwoItems.ps1 script that uses Select-String is seen here.

SearchFileForTwoItems.ps1

If(Select-String -Path C:\fso\HSG62910.txt -Pattern "Windows 2000", "Windows XP")
{"Either Windows 2000 or Windows XP appears in this file"}
Else
{"Neither Windows 2000 or Windows XP appears in this file"}

SA that is all there is to migrating a VBScript to Windows PowerShell. VBScript migration week will continue tomorrow when we will talk about … wait a minute.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • I like using select string to search all the files in a given directory, and give me the name of the file it's in and the line number:

    select-string -pattern "Get-Date" -path *.* | foreach-Object{Write-Host $_.FileName, $_.linenumber}