Learn about Windows PowerShell
Troubleshooting a Windows Powershell Script
Hey Scripting Guy! I tried to use the script from the May 2009 TechNet Magazine article, Hey, Scripting Guy! Working with Access Databases in Windows PowerShell. But when I run it I get an error. This happens both on my Windows Vista computer (64 bit, Service Pack 2) with Office 2007 SP2, and my Windows 2007 build 7100 (64 bit) with Office 2007 SP2 installed. The database I am using has been created by hand. Here is the error I am receiving.
Exception calling "Open" with "1" argument(s): "Provider cannot be found. It may not be properly installed."
At C:\Users\fvandonk\AppData\Local\Temp\Untitled1.ps1:31 char:19
+ $connection.Open <<<< ("Provider = Microsoft.Jet.OLEDB.4.0;Data Source=$Db" )
I get a similar error when I create the database using the script from the Hey Scripting Guy! Article, How Can I Create a Database with More Than One Table? The error from that script is:
Unexpected token 'Provider=' in expression or statement
Can you tell me what I am doing wrong?
-- FD
Hello FD,
The problem is that when I wrote those two scripts, I was using a 32-bit operating system. I have now upgraded to Windows 7 64-bit, and I get the same error messages you are getting. To run the scripts without generating an error, you need to make sure you are using the 32-bit version of Windows PowerShell. On a 64-bit system, you will find both a 32-bit version and a 64-bit version of Windows PowerShell installed. You should see the link for 32-bit Windows PowerShell off the Start menu on Windows 7. Click Accessories and then click Windows PowerShell:
I used Windows PowerShell ISE (x86) and it works fine. I would consider putting something like this at the beginning of the script:
if($env:PROCESSOR_ARCHITECTURE -ne 'x86')
{ 'This script must be run in x86 powershell' ; exit}
Outputting Only the Last Three Lines of a Command
Hey Scripting Guy! I have a request which I believe should be very simple, but all the searching of the Internet has not turned up anything quite right. How can I output only the last three lines of a command into a text document?
For example, I am using the command dir /s to find the total size and number of files and folders in a given directory. These directories can be quite large, so a complete output may cause the text file to exceed 1 GB in size. All I really need is the last three lines that read:
Total Files Listed: 299174 File(s) 1,238,631,539 bytes 27731 Dir(s) 1,667,751,895,040 bytes free
-- KS
Hello KS,
I wrote the GetDirectoryListingStats.vbs script to illustrate what you will need to do. In the GetDirectoryListingStats.vbs script, you first create an instance of the WshShell object as seen here:
Set objShell = CreateObject("WScript.Shell")
You then call the exec method to execute the dir command. To be able to execute the dir command, you also need to call the command interpreter. This is seen here:
Set objExecObject = objShell.Exec(command)
The exec method returns a textstream object. To walk through the textstream object, use the Do Until…Loop as seen here:
Do Until objExecObject.StdOut.AtEndOfStream
strText = objExecObject.StdOut.ReadAll
Loop
Next, you use the split function to break the output into an array that will allow you to return the last few lines. This is shown here:
aryText = Split(strText, VbNewLine)
The complete GetDirectoryListingStats.vbs script is seen here.
GetDirectoryListingStats.vbs
'==========================================================================
'
' NAME: GetDirectoryListingStats.vbs
' AUTHOR: Ed Wilson , Microsoft
' DATE : 6/18/2009
' COMMENT: Uses the exec method from the WshShell object to execute a command
' Uses stdOut to read the output.
'Option Explicit 'is used to force the scripter to declare variables
'On Error Resume Next ‘ go to the next line if it encounters an Error
Dim objShell 'holds WshShell object
Dim objExecObject 'holds what comes back from executing the command
Dim strText 'holds the text stream from the exec command.
Dim command 'the command to run
Dim Directory 'directory to query
Directory = "C:\fso"
command = "cmd /c dir " & Directory
WScript.echo "starting program " & Now ' used to mark when program begins
wscript.Echo "There are " & ubound(aryText) & _
" items in the " & Directory & " Folder"
WScript.Echo aryText(ubound(aryText) -2 )
WScript.Echo aryText(ubound(aryText) -1)
The Measure-Object Cmdlet in Windows PowerShell CTP3
Hey Scripting Guy! The –line switch used with Measure-Object in Windows PowerShell CTP3 seems to only count nonempty lines in .txt files. Is that the supposed behavior?
-- TB
Hello TB,
Let’s take a look. First use the Get-Content cmdlet to read the contents of a text file. This is seen here:
PS C:\Users\edwils> Get-Content 'C:\fso\New Text Document.txt'
line one
line two
line three
We see there are five lines in the text file, but only three have text. Now use the Get-Content cmdlet and pipeline the results to the Measure-Object cmdlet, and tell it to count the number of lines. This is seen here:
PS C:\Users\edwils> Get-Content 'C:\fso\New Text Document.txt' | Measure-Object -Line
Lines Words Characters Property
----- ----- ---------- --------
3
As you can see, Measure-Object tells us we have three lines. Lets now try one more thing: Use Get-Content and store the contents into a text file. Now use the count property on the file. This is seen here:
PS C:\Users\edwils> $file = Get-Content 'C:\fso\New Text Document.txt'
PS C:\Users\edwils> $file.Count
5
TB you are correct. The Measure-Object cmdlet only counts lines; it does not count blank lines. If you need to get blank lines in a text file, you can use the count property.
Troubleshooting Output from a VBScript Script
Hey Scripting Guy! I am having an issue with outputting results. My code is reading server names from a .txt file and outputting the logged-on terminal server users and the server name they are logged onto. My code looks like this:
GetTerminalServerUsersAndServers.vbs
Const ForReading = 1
Const wbemImpersonationLevelImpersonate = 3
Const wbemAuthenticationLevelPktPrivacy = 6
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile("servers.txt", ForReading)
Do Until objTextFile.AtEndOfStream
strComputer = objTextFile.Readline
Set objLocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMI = objLocator.ConnectServer (strComputer, "root\cimv2")
objWMI.Security_.ImpersonationLevel = wbemImpersonationLevelImpersonate
objWMI.Security_.AuthenticationLevel = wbemAuthenticationLevelPktPrivacy
Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colSessions = objWMI.ExecQuery _
("Select * from Win32_LogonSession Where LogonType = 10")
If colSessions.Count = 0 Then
Wscript.Echo "No interactive user found"
Else
For Each objSession in colSessions
Set colList = objWMI.ExecQuery("Associators of " _
& "{Win32_LogonSession.LogonId=" & objSession.LogonId & "} " _
& "Where AssocClass=Win32_LoggedOnUser Role=Dependent" )
For Each objItem in colList
Wscript.Echo "Server Name: " & strComputer
WScript.Echo objItem.Name
Next
End If
The problem is with the output. It is coming out like this (shortened):
Server Name: ctx-07
carrie
diane
rachel
Server Name: ctx-09
sandra
kathy
Linda
Instead, I would like to see the users group by server, as seen here:
I think I have an idea how to do this—with some kind of Do loop—but I am not confident my solution is correct. Could you point me in the right direction?
-- WM
Hello WM,
Your problem is that you are emitting the data from inside the For…Each…Next loop. Each time you pass through the loop, the data is being displayed. Instead, what you need to do is capture the information from inside the loop and wait until you are done looping to display the information. Something like this would be relatively easy to do:
GetTerminalServerUsersAndServersRevised.vbs
colNames = colNames & VBCRLF & objItem.Name
Wscript.Echo colNames
We come to the end of another Quick-Hits Friday, which also brings to an end another week at the Script Center. Join us on Monday for another fun-filled and exciting week in the world of scripting. Until then, have a great weekend.
Ed Wilson and Craig Liebendorfer, Scripting Guys