Why are variable assignments within a Trap block not visible outside it? - powershell

Why are the variable assignments that I make inside the Trap block not visible outside it?
$integer = 0;
$string = [String]::Empty;
$stringBuilder = new-object 'System.Text.StringBuilder';
trap
{
$integer = 1;
$string = '1';
$stringBuilder.Append('1');
write-host "Integer Variable Inside: " $integer;
write-host "String Variable Inside: " $string;
write-host "StringBuilder Variable Inside: " $stringBuilder;
continue;
}
$dummy = 1/$zero;
write-host "Integer Variable Outside: " $integer;
write-host "String Variable Outside: " $string;
write-host "StringBuilder Variable Outside: " $stringBuilder;
I would have expected the results from within and outside the Trap block to be identical but these are the results that I am seeing.
Integer Variable Inside: 1
String Variable Inside: 1
StringBuilder Variable Inside: 1
Integer Variable Outside: 0
String Variable Outside:
StringBuilder Variable Outside: 1
Notice that it is only the StringBuilder that retains its value.
I am guessing that this has something to do with the difference between value and reference types but can't quite pin it down.

With info that slipsec provided above and through some further experimentation, I now understand what is happening here.
Joel explains how the Trap scope works as follows.
Even though in our error handler we
were able to access the value of
$Result and see that it was True … and
even though we set it to $False, and
printed it out so you could see it was
set … the function still returns True,
because the trap scope doesn’t modify
the external scope unless you
explicitly set the scope of a
variable. NOTE: If you had used
$script:result instead of $result (in
every instance where $result appears
in that script), you would get the
output which the string/comments led
you to expect.
So variables from outside the Trap scope can be read but not set because they are copies of the originals (thanks Jason). This is the reason why the Integer variable did not retain its value. The StringBuilder however, is a reference object and the variable is only a pointer to that object. The code within the Trap scope was able to read the reference that the variable was set to and modify the object to which it was pointing - the variable itself required no change.
Note that Joel's tip about specifying the scope of the variable allowed me to set the value of the Integer variable from within the Trap scope.
$script:integer = 0;
$string = [String]::Empty;
$stringBuilder = new-object 'System.Text.StringBuilder';
trap
{
$script:integer = 1;
$string = '1';
$stringBuilder.Append('1');
write-host "Integer Variable Inside: " $script:integer;
write-host "String Variable Inside: " $string;
write-host "StringBuilder Variable Inside: " $stringBuilder;
continue;
}
$dummy = 1/$zero;
write-host "Integer Variable Outside: " $script:integer;
write-host "String Variable Outside: " $string;
write-host "StringBuilder Variable Outside: " $stringBuilder;
...and these are the results.
Integer Variable Inside: 1
String Variable Inside: 1
StringBuilder Variable Inside: 1
Integer Variable Outside: 1
String Variable Outside:
StringBuilder Variable Outside: 1
Note that the string variable does not retain its value because although it is a reference type, it is also immutable.

Rather than re-write Jakul's excelent post on the subject, I'll just link it:
http://huddledmasses.org/trap-exception-in-powershell/
Gobs of information on how powershell deals with error handling with plenty of detail.

Related

Not getting exact value in Variable becasue value contains $ char

I have a string "/Name Pa$Name#my"
$myname = GetContent("Name")
GetContent is only gets the key and gives the value for that Key. I cannot modify the GetContent function.
If i execute above, as "Pa$Name#my" contains the '$' char, $myname gives me value as "Pa#my" and ignores "$Name" content.
What can i do to get the $myname = Pa$Name#my ? Can i append some special characters while assigning the $myname variable.
As far as I understand your trouble comes from the fact that the var $name does not exist. You can use single qotes if you don't want Powershell to look for vars :
$a = 'Pa$Name#my'

Explain scoping for functions called from PowerShell closures

The following PowerShell code displays unexpected scoping behavior for functions called from closures. Can you explain whether this is "by design", or is a defect?
function runblock($block) {
$x = 4
& $block
}
function printx() {
" in printx: x=" + $x
}
"PSVersion $($PSVersionTable.PSVersion)"
$x = 1
$b = {"In block x=" + $x ; $x = 3 ; printx}
$x = 2
runblock $b
$x = 1
$b = {"In closure x=" + $x ; $x = 3 ; printx}.GetNewClosure()
$x = 2
runblock $b
Output from the above is
PSVersion 3.0
In block x=4
in printx: x=3
In closure x=1
in printx: x=4
Most of the output makes sense to me:
The script block outputs In block x=4 since its parent scope is the runblock function. The printx function outputs x=3 since its parent scope is the script block scope.
The closure outputs In closure x=1 since the value of $x was captured by GetNewClosure call. All as expected.
BUT:
The call to printx from the closure outputs in printx: x=4. So the scope that printx executes within is unaffected by the scope of the closure where $x = 3.
It seems strange to me that a function called from a normal script block does see the variables in the script block scope, but a function called from a closure does not see the variables in the closure.
Consider following code:
function Run {
param($ScriptBlock)
$a = 3
# Picture 4
& $ScriptBlock
}
function Print {
# Picture 6
"Print `$a=$a"
"Print `$b=$b"
}
$a = 1
$b = 1
# Picture 1
$SB = {
# Picture 5
"Closure `$a=$a"
"Closure `$b=$b"
Print
}.GetNewClosure()
# Picture 2
$a = 2
$b = 2
# Picture 3
Run $SB
It prints:
Closure $a=1
Closure $b=1
Print $a=3
Print $b=2
Picture 1: you start from global scope, where you define some variables.
Picture 2: GetNewClosure() create new module and copy variables to it. (red arrow show parent scope relationship)
Picture 3: you change value of variables. Module scope not affected.
Picture 4: function Run called. It create local variable $a. (blue arrow show call direction)
Picture 5: $SB script block called. Script block bound to module session state, so you transit to it.
Picture 6: function Print called. Function bound to global session state, so you return to it.

Global variable changed in function not effective

I just tried this code:
$number = 2
Function Convert-Foo {
$number = 3
}
Convert-Foo
$number
I expected that function Convert-Foo would change $number to 3, but it is still 2.
Why isn't the global variable $number changed to 3 by the function?
No, I'm afraid PowerShell isn't designed that way. You have to think in scopes, for more information on this topic please read the PowerShell help about scopes or type Get-Help about_scopes in your PowerShell ISE/Console.
The short answer is that if you want to change a variable that is in the global scope, you should address the global scope:
$number = 2
Function Convert-Foo {
$global:number = 3
}
Convert-Foo
$number
All variables created inside a Function are not visible outside of the function, unless you explicitly defined them as Script or Global. It's best practice to save the result of a function in another variable, so you can use it in the script scope:
$number = 5
Function Convert-Foo {
# do manipulations in the function
# and return the new value
$number * 10
}
$result = Convert-Foo
# Now you can use the value outside the function:
"The result of the function is '$result'"

any way to retrieve a circular reference value

I wrote th following program to see feature called circular reference in perl.
my $foo = 100;
$foo = \$foo;
print "Value of foo is : ", $$foo, " " , $foo;
the output was:
Value of foo is : REF(0x21b632c) REF(0x21b632c)
but i was wondering where the value 100 gone can any one help me.or is it a memory leak?
The value 100 was overwritten. When you assign a value into a variable, it replaces the previous value.
my $foo = 100;
$foo = "a string here";
The value 100 has gone. The same thing happens in your code above. The only difference is what kind of value was put in there.

How does the closure mechanism work in Perl?

Newbie in Perl again here, trying to understand closure in Perl.
So here's an example of code which I don't understand:
sub make_saying {
my $salute = shift;
my $newfunc = sub {
my $target = shift;
print "$salute, $target!\n";
};
return $newfunc; # Return a closure
}
$f = make_saying("Howdy"); # Create a closure
$g = make_saying("Greetings"); # Create another closure
# Time passes...
$f->("world");
$g->("earthlings");
So my questions are:
If a variable is assigned to a function, is it automatically a reference to that function?
In that above code, can I write $f = \make_saying("Howdy") instead? And when can I use the & because I tried using that in passing the parameters (&$f("world")) but it doesn't work.
and lastly, in that code above how did the strings world and earthlings get appended to the strings Howdy and Greetings.
Note: I understand that $f is somewhat bound to the function with the parameter "Howdy" so that's my understanding how "world" got appended. What I don't understand is the 2nd function inside. How that one operates its magic. Sorry I really don't know how to ask this one.
In Perl, scalar variables cannot hold subroutines directly, they can only hold references. This is very much like scalars cannot hold arrays or hashes, only arrayrefs or hashrefs.
The sub { ... } evaluates to a coderef, so you can directly assign it to a scalar variable. If you want to assign a named function (e.g. foo), you have to obtain the reference like \&foo.
You can call coderefs like $code->(#args) or &$code(#args).
The code
$f = \make_saying("Howdy")
evaluates make_saying("Howdy"), and takes a reference to the returned value. So you get a reference that points to a coderef, not a coderef itself.
Therefore, it can't be called like &$f("world"), you need to dereference one extra level: &$$f("world").
A closure is a function that is bound to a certain environment.
The environment consists of all currently visible variables, so a closure always remembers that scope. In the code
my $x;
sub foo {
my $y;
return sub { "$x, $y" };
}
foo is a closure over $x, as the outer environment consists of $x. The inner sub is a closure over $x and $y.
Each time foo is executed, we get a new $y and therefore a new closure. Most importantly $y does not "go out of scope" when foo is left, but it will be "kept alive" as it's still reachable from the closure returned.
In short: $y is a "local state" of the closure returned.
When we execute make_saying("Howdy"), the $salute variable is set to Howdy. The returned closure remembers that scope.
When we execute it again with make_saying("Greetings"), the body of make_saying is evaluated again. The $salute is now set to Greetings, and the inner sub closes over this variable. This variable is separate from the previous $salute, which still exists, but isn't accessible except through the first closure.
The two greeters have closed over separate $salute variables. When they are executed, their respective $salute is still in scope, and they can access and modify the value.
If a variable is asigned to a function, is it automatically a
reference to that function?
No. In example the function make_saying return reference another function. Such closures do not have name and can catch a variable from outside its scope (variable $salute in your example).
In that above code, can i write $f = \make_saying("Howdy") instead?
And when can i use the & cause i tried using that in passing the
parameters (&$f("world")) but it doesnt work.
No. $f = \make_saying("Howdy") is not what you think (read amon post for details). You can write $f = \&make_saying; which means "put into $f reference to function make_saying". You can use it later like this:
my $f = \&make_saying;
my $other_f = $f->("Howdy");
$other_f->("world");
and lastly, In that code above how in he** did the words world and
earthlings got appended to the words howdy and greetings.
make_saying creating my variable which goes into lamda (my $newfunc = sub); that lambda is returned from make_saying. It holds the given word "Howdy" through "closing" (? sorry dont know which word in english).
Every time you call the subroutine 'make_saying', it:
creates a DIFFERENT closure
assigns the received parameter to the scalar '$salute'
defines (creates but not execute) an inner anonymous subroutine:
That is the reason why at that moment nothing is assigned to the scalar
$target nor is the statement print "$salute, $target!\n"; executed .
finally the subroutine 'make_saying' returns a reference to the inner anonymous subroutine, that reference becomes the only way to call the (specific) anonymous subroutine.
Ever time you call each anonymous subroutine, it:
assign the received parameter to the scalar $target
sees also the scalar $salute that will have the value assigned at the moment in which was created the anonymous subroutine (when was called its parent subroutine make_saying
finally executes the statement print "$salute, $target!\n";