循环中的Powershell值会丢失其值

时间:2011-06-03 17:57:45

标签: powershell

原谅标题,我不确定如何解释我所看到的内容。

示例代码:

    $SampleValues = 1..5
    $Result = "" | Select ID
    $Results = @()  

    $SampleValues | %{
        $Result.ID = $_
        $Results += $Result
    }

$Results

这非常简单:

  • 创建一个包含5个数字的数组,以便在循环中使用
  • 使用名为ID
  • 的NoteProperty创建临时变量
  • 创建一个空数组来存储结果
  • 遍历5个数字中的每个数字,将它们分配给临时变量,然后将其附加到数组中。

预期结果是1,2,3,4,5,但运行时返回5,5,5,5,5

这是一个来自更复杂脚本的准系统示例,我试图弄清楚结果是什么原因。在每次迭代中,已添加到$ Results的所有元素都将其值更新为最新值。我已经测试了强制所有内容到$ Script:或$ Global:scope并得到相同的结果。

我找到的唯一解决方案是将$ Result声明移动到循环中。

    $SampleValues = 1..5
    $Results = @()

$SampleValues | %{
    $Result = "" | Select ID
    $Result.ID = $_
    $Results += $Result
    }

这样可行(你的结果会得到1,2,3,4,5)。它看起来像$ Results只是持有多个对单个$ Result对象的引用,但是为什么将它移动到循环中可以解决问题呢?在这个例子中,$ Result是一个字符串,所以也许每次迭代都会创建一个新对象,但即使我强制$ Result为一个整数(由于整数不像字符串那样是不可变的,因此不应该重新创建一个新对象)仍然解决了问题,我得到了我期望的结果。

如果有人对这个解决问题的确切原因有任何了解,我会非常好奇。我有很多替代方案可以实现,但没有明确地理解为什么这样做会让我烦恼。

2 个答案:

答案 0 :(得分:8)

它通过进入循环来解决问题,因为那时你每次都要创建一个新的$ Result对象,而不是在同一个上更改一个值(在数组中引用了5次)。

它与您使用"" | Select ID还是123 | Select ID没有任何关系,因为它只是PSCustomObject上的一种属性,它仍然是引用类型而不是值类型。

请记住,Powershell内部都是.NET。这里有一些C#类似于Powershell在你的第一个例子中所做的事情,结果是所有5个(希望你知道C#):

var SampleValues = new []{1,2,3,4,5};
var Result = new CustomObject(){ ID = "" };
var Results = new List<Object>();

foreach (var _ in SampleValues) {
    Result.ID = _;
    Results.Add(Result);
}

希望你能看到var Result = new CustomObject(){ ID = "" }循环中移动foreach如何使其更好地工作,同样的概念在Powershell中也适用。

答案 1 :(得分:1)

在这个示例中,$ Result是一个字符串,所以它可能每次迭代都会创建一个新对象,但即使我强制$ Result为一个整数(由于整数不是因为它不能重新创建一个新对象像字符串一样不可变)它仍然解决了问题,我得到了我期望的结果。

实际上,在您的示例中,$ Result不是字符串。它是一个通用对象,其中一个属性(ID)是一个字符串。

Powershell变量有两种类型 - 值和引用。字符串或整数按值传递,对象通过引用传递。通过addind $ result到$ results 5次,你可以获得5个参考结果,而不是5个不同的对象。

如果我们将$ result.id属性(整数/值类型)添加到$ results而不是$ result对象,我们得到:

 $SampleValues = 1..5
     $Result = "" | Select ID
     $Results = @()  

     $SampleValues | %{
         $Result.ID = $_
         $Results += $Result.ID
     }

 1
 2
 3
 4
 5