Running script in PowerGUI vs PS

by jmp8600 at 2013-03-19 10:45:10

Hello There,

I am newbie to this site and i am reading Learn PowerShell in Month of Lunches by Don. I have completed chapter 20. I have created below script which does pretty basic stuff. However the script works fine in PowerGUI but I am get different output when i run from the shell.

i saved below as Get-DiskType.ps1

Param ($cname = ‘localhost’)
gwmi win32_Logicaldisk -ComputerName $cname | select DeviceID,
@{l=‘Disk Type’;e={Get-DriveInfo $_.DriveType}}|ft -AutoSize

Function Get-DriveInfo{
Param (
$DriveType
)
switch ($DriveType){
0 {
Return ‘Unknown’
}
1 {
Return ‘No Root Directory’
}
2 {
Return ‘Removable Disk’
}
3 {
Return ‘Local Disk’
}
4 {
Return ‘Network Drive’
}
5 {
Return ‘Compact Disc’
}
6 {
Return ‘RAM Disk’
}
}
}

output from PowerGU which is expected:
DeviceID Disk Type
-------- ---------
A: Removable Disk
C: Local Disk
D: Compact Disc

Output from PowerShell which is incorrect]
PS C:\Users\administrator\documents> .\Get-DiskType.ps1

DeviceID Disk Type
-------- ---------
A:
C:
D:

Can someone please tell me what am i missing here? i am not able to figure out why PowerGUI is working and in PS its not.
by MasterOfTheHat at 2013-03-19 11:40:33
Well, jmp, I’ll bet that the code doesn’t work the first time you execute it in PowerGUI, but it does on subsequent runs? And I’ll bet it never runs in the console?

A function has to be defined before it can be called. That means that the function either has to be available to the current scope via an imported module or it has to be defined in the script you are executing before the line it is called on. So your code doesn’t work in the console nor on the first run in PowerGUI because Get-DriveInfo isn’t defined until after the line that calls that function, (the gwmi line). The reason it works on runs after that first on in PowerGUI is because PowerGUI actually loads that function into the current session when the script is run and doesn’t remove it after the script is executed.

To prove it, load up a fresh PowerGUI window and don’t open any script files.
On the console in PowerGUI, run "gci function:get*". You’ll see that your Get-DriveInfo function doesn’t show up.
> gci function:get*

CommandType Name ModuleName
----------- ---- ----------
Function Get-Verb


Now open your script, and run it, (it shouldn’t work):
DeviceID Disk Type
-------- ---------
C:
D:
E:
F:
G:
H:
I:
J:
N:
W:
Z:


Then, run gci function:get* again. You’ll see that your function is there, even though your script has finished executing.
> gci function:get*

CommandType Name ModuleName
----------- ---- ----------
Function Get-Verb
Function Get-DriveInfo

If you want to unload your function from the session now, run "Remove-Item function:Get-DriveInfo" in the PowerGUI console window. Run "gci function:get*" again to prove it’s gone:
>Remove-Item function:Get-DriveInfo
> gci function:get*

CommandType Name ModuleName
----------- ---- ----------
Function Get-Verb


Now, to fix the problem with your script move the function definition up between the Param line and the gwmi line. :slight_smile:
Param ($cname = 'localhost')
Function Get-DriveInfo{
Param (
$DriveType
)
switch ($DriveType){
0 {
Return 'Unknown'
}
1 {
Return 'No Root Directory'
}
2 {
Return 'Removable Disk'
}
3 {
Return 'Local Disk'
}
4 {
Return 'Network Drive'
}
5 {
Return 'Compact Disc'
}
6 {
Return 'RAM Disk'
}
}
}

gwmi win32_Logicaldisk -ComputerName $cname | select DeviceID,@{l='Disk Type';e={Get-DriveInfo $_.DriveType}}|ft -AutoSize
by poshoholic at 2013-03-19 11:48:26
Hi there,

Thanks for sharing your script. I was able to figure out what is going wrong here, but it took some digging because your problem highlights what I would consider to be a PowerShell "gotcha".

The "gotcha" is this: errors that are raised inside custom property definitions in Select-Object are completely hidden from the user. In your script, you’re creating a custom property called "Disk Type".

That property is assigned a value that is returned from Get-DriveInfo. But Get-DriveInfo is not guaranteed to exist the moment that it is called, because it is declared after its invocation. If you invoke your script by simply referencing it on a local path, the "Disk Type" property ends up blank as a result. If you dot-source your script just once (which PowerGUI does), at that point Get-DriveInfo will be loaded into the current session and all subsequent invocations of your script will work. Therefore the fact that it "works" in PowerGUI is actually just luck in this case.

The solution is simple: move your Get-DriveInfo function declaration up in your script so that it is defined before your pipeline where you invoke it.

Also, here’s a tip with PowerShell debugging that helped me find the root cause of this problem. When you have a script that isn’t running as you expect it to, do the following:

1. Before you invoke your script, invoke $error.Clear() to clear the error variable.
2. Invoke your script.
3. Invoke $error to see if PowerShell actually logged any errors during the invocation of your script that were hidden by PowerShell itself or by use of -ErrorAction SilentlyContinue or other means.

In this case if you follow those steps, $error shows you the error that is being swallowed by PowerShell, which highlights that Get-DriveInfo does not exist at the time that it is invoked.
by poshoholic at 2013-03-19 11:49:46
Whoops, looks like MasterOfTheHat pushed his reply out first. Both of us highlight the same issue. Note the tip at the end of my reply though, it helps get to the bottom of such problems more quickly.
by jmp8600 at 2013-03-19 12:16:53
Thank you so much. its very clear now. Both of you are correct.