PowerShell pass by reference not working for me - powershell

I'm trying to make a simple swap function in PowerShell, but passing by reference doesn't seem to work for me.
function swap ([ref]$object1, [ref]$object2){
$tmp = $object1.value
$object1.value = $object2.value
$object2.value = $tmp
}
$a = 1
$b = 2
$a, $b
swap ([ref]$a) ,([ref]$b)
$a, $b
This SHOULD work, but no...
Output:
1
2
1
2
What did I do wrong?

Call like this:
swap ([ref]$a) ([ref]$b)
The mistake of using , is described in the Common Gotchas for PowerShell here on Stack Overflow.

By the way, PowerShell has a special syntax to swap values, and there isn't a need to use $tmp:
$a,$b = $b,$a

Firstly, you're calling it wrong. Putting a comma in the call to swap means you're passing an array of them to objects as the first parameter. If you were to correct it...
swap ([ref]$a) ([ref]$b)
...it would then work.

Related

Perl Split Function - How to assign a specify array value to a variable in one line

Is this a way of using a one-liner to assign an array value to a variable using the split function.
I have the following code. This works.
my #Fields = split(',',#_[0]);
my $Unit = #Fields[2];
Wondering if it could be written in one like the following, they are returning syntax errors.
my $Unit = split(',',#_[0])[2];
my $Unit = #(split(',',#_[0]))[2];
Your second one is very close. Drop that leading #:
my $Unit = (split(',', $_[0]))[2];
(Note $_[0] instead of the one-element array slice #_[0]... if you have use warnings; in effect like you should, that would give you one.)

Append PowerShell function args to program invoked by the function

I'm wondering how I can append PowerShell function arguments to a program command?
I want something like this:
function foo($x, $y, $z) {
docker run $x $y $z
}
So that calling PS>foo alone would be the equivelant to PS>docker run, and PS>foo a b c to PS>docker run a b c.
This seems like a question that must have an answer here somewhere, but I am unable to find it. I'm not sure whether I'm just phrasing my searches poorly. Apologies in advance if so.
Thanks!
Grab the argument values from $PSBoundParameters.Values:
function foo($x, $y, $z) {
docker run $PSBoundParameters.Value
}
As pointed out in the comments, $PSBoundParameters doesn't guarantee insertion order, an alternative approach would be to take a single argument of arrays with the ValueFromRemainingArguments parameter flag set:
function foo {
param(
[Parameter(Mandatory=$false,ValueFromRemainingArgumemnts)]
[string[]]$dockerArgs
)
$dockerArgs = #('run';$dockerArgs)
& docker $dockerArgs
}

'=' is working in place of 'eq'

Hi I am writing a perl script to accomplish some task.In my script I am using one if loop to compare two strings as shown below.
if($feed_type eq "SE"){
...........}
The above code is not giving me any warning but the output is not as I expected.
Instead of 'eq' if I use '=' I am getting a warning saying expectng '==' but '=' is present. But I am getting the expected output.
Ideally for string comparison I must use 'eq' and for numbers '=='. In this case it's not working. Can anyone figure out what is the problem here?
More info:
This if loop is present in a subroutine. $feed_type is an input for this subroutine. I am reading the input as below:
my $feed_type=#_;
The problem is fixed. I just changed the assignemet statement of feed_type as below
my $feed_type=$_[0];
and it's reading the value as SE and the code is working.
but I still dont know why my $feed_type=$_[0]; didn't work.
= might well work in place of eq, but not for the reason you think.
#!/usr/bin/env perl
use strict;
use warnings;
my $test = "fish";
my $compare = "carrot";
if ( $test = $compare ) {
print "It worked\n";
}
Of course, the problem is - it'll always work, because you're testing the result of an assignment operation.*
* OK, sometimes assignment operations don't work - this is why some coding styles suggest testing if ( 2 == $result ) rather than the other way around.
This is about a core Perl concept: Context. Operators and functions work differently depending on context. In this case:
my $feed_type = #_;
You are assigning an array in scalar context to the variable. An array in scalar context returns its size, not the elements in it. For this assignment to work as you expect, you have to either directly access the scalar value you want, like you have suggested:
my $feed_type = $_[0];
...or you can put your variable in list context by adding parentheses:
my ($feed_type) = #_;
This has the benefit of allowing you to perform complex assignments, like this:
my ($first, $second, #rest) = #_;
So, in short, the problem was that your comparison that looked like this:
if($feed_type eq "SE")
Was actually doing this:
if(1 eq "SE")
And returning false. Which is true. Consider this self-documenting code:
sub foo {
my $size = #_;
if ($size == 1) {
warn "You passed 1 argument to 'foo'\n";
return;
}
}
Which demonstrates the functionality you inadvertently used.
= is used to assign the variable a value, so you would need '==' to compare numerical values and 'eq' for strings.
If it's complaining about not using '==', then it's because $feed_type is not a string.
I can't tell as there's no more code. Whatever $feed_type is set by you need to confirm it actually contains a string or if you're even referencing it correctly.

Is it possible to dynamically expand a scalar assignment depending on the size of an array?

So, here's the deal. I have an array, let's call it
#array = ('string1','string2','string3','string4');
etc., etc. I have no way of knowing how large the array is or what the contents are, specifically, except that it is an array of strings.
I also have a variable which needs to be changed depending on the size and contents of the array.
Here's a sample easy assignment of that variable, along with the array that would have generated the assignment:
#array = ('string1','string2','string3');
$var = Some::Obj1(Some::Obj2('string1'),
Some::Obj2('string2'),
Some::Obj2('string3'));
Then, if for instance, I had the following #array,
#array = ('string1','string2','string3','string4','string5');
My assignment would need to look like this:
$var = Some::Obj1(Some::Obj2('string1'),
Some::Obj2('string2'),
Some::Obj2('string3'),
Some::Obj2('string4'),
Some::Obj2('string5'));
Can you guys think of any way that something like this could be implemented?
Well, if all you need is to turn some strings into a list of objects inside an object... Why not map?
my #array = ('string1','string2','string3','string4','string5');
my $var = Some::Obj1(map { Some::Obj2($_) } #array);
Yes, you just do
$var = Some::Obj1(map(Some::Obj2($_), #array));
That produces the exact same result as the code you wrote:
$var = Some::Obj1(Some::Obj2('string1'),
Some::Obj2('string2'),
Some::Obj2('string3'),
Some::Obj2('string4'),
Some::Obj2('string5'));
Of course, it goes without saying that you should use either my or our before the variable as appropriate if you are initializing it for the first time. If you wish to perform more complicated operations using map, an entire block of code can be enclosed in braces and the comma omitted, i.e.,
map {operation 1; operation 2; ...; final operation stored as result;} #array

Assignment inside Perl ternary conditional operator problems

This snippet of Perl code in my program is giving the wrong result.
$condition ? $a = 2 : $a = 3 ;
print $a;
No matter what the value of $condition is, the output is always 3, how come?
This is explained in the Perl documentation.
Because of Perl operator precedence the statement is being parsed as
($condition ? $a= 2 : $a ) = 3 ;
Because the ?: operator produces an assignable result, 3 is assigned to the result of the condition.
When $condition is true this means ($a=2)=3 giving $a=3
When $condition is false this means ($a)=3 giving $a=3
The correct way to write this is
$a = ( $condition ? 2 : 3 );
print $a;
We got bitten by this at work, so I am posting here hoping others will find it useful.
Once you have an inkling that you might be suffering from precedence problems, a trick to figure out what Perl thought you meant:
perl -MO=Deparse,-p -e '$condition ? $a= 2 : $a= 3 ; print $a;'
In your case, that'll show you:
(($condition ? ($a = 2) : $a) = 3);
print($a);
-e syntax OK
...at which point you should be saying "oh, that explains it"!
Just to extend the previous answer... If, for whatever reason, the assignments need to be part of the conditional, you'd want to write it thusly:
$condition ? ($a=2) : ($a=3);
This would be useful if you're assigning to different variables based on the condition.
$condition ? ($a=2) : ($b=3);
And if you're choosing the variable, but assigning the same thing no matter what, you could even do this:
($condition ? $a : $b) = 3;
Because of Perl operator precedence the statement is being parsed as:
($condition ? $a = 2 : $a ) = 3 ;
Because the ?: operator produces an assignable result, 3 is assigned to the result of the condition.
When $condition is true this means $a=2=3 giving $a=3
When $condition is false this means $a=3 giving $a=3
The correct way to write this is
$a = $condition ? 2 : 3;
In general, you should really get out of the habit of using conditionals to do assignment, as in the original example -- it's the sort of thing that leads to Perl getting a reputation for being write-only.
A good rule of thumb is that conditionals are only for simple values, never expressions with side effects. When you or someone else needs to read this code eight months from now, would you prefer it to read like this?
$x < 3 ? foo($x) : bar($y);
Or like this?
if ($x < 3) {
$foo($x);
} else {
$bar($y);
}
One suggestion to Tithonium's answer above:
If you are want to assign different values to the same variable, this might be better (the copy-book way):
$a = ($condition) ? 2 : 3;