4

Why do the following 3 lines run without error from the PowerShell prompt, but return an error when run in a script (foo.ps1)? In both cases, $b -eq $null returns $true and $b.GetType() returns an error for invoking on $null, but there is something different about the $b in the interactive session.

$a = 1,2,3
[array]$b = $a | where {$false}
$b | where {$_.GetType()}

When run as script, the last line returns

You cannot call a method on a null valued expression.

I ran into this during ill-fated attempts to prevent array unrolling. Removing [array] makes the error go away, and I'll move on to trying to better understand the unrolling rules (I want $b to be an empty array, not $null), but I'd like to understand the reason for the difference here.

aggieNick02
  • 2,557
  • 2
  • 23
  • 36
  • 1
    `$a` is a scriptblock. Is that on purpose for this? Why the `where {$false}`? `$b` should be null in this example since the where block lets nothing through the pipe. – Matt Jun 20 '17 at 15:21
  • [related](https://stackoverflow.com/questions/20022976/difference-between-powershell-console-and-powershell-ise) – Matt Jun 20 '17 at 15:24
  • Didn't mean to make $a a scriptblock, sorry. It was meant to be an array. I've updated it. Fortunately it doesn't affect the result and underlying question. – aggieNick02 Jun 20 '17 at 15:46
  • $b is indeed $null. But the $b run in a script cannot be piped to the where without error. But it can be piped to the where interactively just fine... – aggieNick02 Jun 20 '17 at 15:46
  • 1
    `$b = @($a | where {$false})`, also, [that](https://stackoverflow.com/q/30016949) can be relevant to your question. – user4003407 Jun 20 '17 at 18:47
  • Thanks @PetSerAl. All of my [array]$foo lines are now $foo = @(...) lines, and that link is great. Unless I'm missing something though, it doesn't seem to cover why interactive vs script behaves different, does it? – aggieNick02 Jun 21 '17 at 14:19
  • 1
    @aggieNick02 It is not *interactive vs script*, but new scope vs same scope: `& { $a = 1,2,3; [array]$b = $a | where {$false}; $b | where {$_.GetType()} }` vs `. { $a = 1,2,3; [array]$b = $a | where {$false}; $b | where {$_.GetType()} }`. IMHO, possibly some bug here. – user4003407 Jun 21 '17 at 17:02
  • @PetSerAl - thanks. Is the `&` version staying in global scope while the `.` version creates a new local scope? – aggieNick02 Jun 21 '17 at 18:55
  • @aggieNick02 No, `&` create new scope, while `.` use current scope, which can be global, but this is not necessary. – user4003407 Jun 21 '17 at 18:58

2 Answers2

1

There is a perfect explanation

  1. By typing [array] you tell the variable to be strongly typed. I suspect this line in .NET code, triggers the exception as it needs a type as a variable... http://referencesource.microsoft.com/#mscorlib/system/array.cs,72
demokritos
  • 1,416
  • 2
  • 12
  • 34
  • Thanks for the link. It is definitely interesting and relevant, but unless I'm missing something, I don't think it addresses why the behavior is different in script vs. powershell prompt, does it? – aggieNick02 Jun 21 '17 at 14:18
  • Nope, you are right, missed the main point. Weird, in a script block it does error, i.e. if you put into a function/script block, it does error on the prompt as well.. It should be with the outputting data.. – demokritos Jun 21 '17 at 14:37
0

If you're running this from the ISE or from interactive, the variables are being saved. In your examples, I'm not sure why you're using Where-Object instead of %/ForEach-Object. Working on what I think you're attempting to do:

$a = @(1, 2, 3)
[Array]$b
$a | % { $b += $_ }
$b | % { $_.GetType() }
Maximilian Burszley
  • 18,243
  • 4
  • 34
  • 63
  • In real life, I'm trying to take a list of things, filter it down, and then pass it to an operation. Sometimes the filtering results in an empty result (nothing matches the filter), and this leads to an error. I'd like it to lead to nothing. I can work around this in a number of ways. What bothers me is that I don't understand the difference in behavior... – aggieNick02 Jun 20 '17 at 15:50
  • 1
    The real-life example is getting disks, filtering them, and then running Clear-Disk. I'd imagine folks would be much less likely to try out that script. ;-) – aggieNick02 Jun 20 '17 at 15:51
  • @aggieNick02 It sounds like you need error handling in that case. Try using `{ Try { $_.GetType() } Catch { Write "$_.Exception.Message" } }` – Maximilian Burszley Jun 20 '17 at 16:30