首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PowerShell:修改数组元素

PowerShell:修改数组元素
EN

Stack Overflow用户
提问于 2015-12-08 21:06:44
回答 2查看 42.9K关注 0票数 5

My get-objects返回具有公共属性的MyObject数组:

代码语言:javascript
复制
public class MyObject{
    public string testString = "test";
}

我希望没有编程技能的用户能够从数组的所有对象中修改公共属性(如本例中的testString )。然后将修改后的数组提供给我的第二个cmdlet,它将对象保存到数据库中。

这意味着“编辑代码”的语法必须尽可能简单。

看起来应该是这样的:

代码语言:javascript
复制
> get-objects | foreach{$_.testString = "newValue"} | set-objects

我知道这是不可能的,因为只需从数组中返回元素的副本。

因此,您需要在循环中按索引访问元素,然后修改property.This变得非常迅速,对于不熟悉编程的人来说非常复杂。

是否有任何“用户友好”的内置方式来做到这一点?它不应该比简单的foreach {property = value}更“复杂”

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-12-08 22:07:37

我知道这是不可能的,因为$_只是从数组(https://social.technet.microsoft.com/forums/scriptcenter/en-US/a0a92149-d257-4751-8c2c-4c1622e78aa2/powershell-modifying-array-elements)返回元素的副本。

我觉得你把答案错记在那条线上了。

$_确实是当前正在迭代的任何枚举数返回的值的本地副本,但您仍然可以返回该值的修改副本(作为在评论中指出):

代码语言:javascript
复制
Get-Objects | ForEach-Object {
    # modify the current item
    $_.propertyname = "value"
    # drop the modified object back into the pipeline
    $_
} | Set-Objects

在(据称不可能)需要修改存储的对象数组的情况下,可以使用相同的技术用新值覆盖数组:

代码语言:javascript
复制
PS C:\> $myArray = 1,2,3,4,5
PS C:\> $myArray = $myArray |ForEach-Object {
>>>    $_ *= 10
>>>    $_
>>>}
>>>
PS C:\> $myArray
10
20
30
40
50

这意味着“编辑代码”的语法必须尽可能简单。

谢天谢地,PowerShell在内省方面非常强大。您可以实现一个包装器函数,它将$_;语句添加到循环主体的末尾,以防用户忘记:

代码语言:javascript
复制
function Add-PsItem 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromRemainingArguments)]
        [psobject[]]$InputObject,

        [Parameter(Mandatory)]
        [scriptblock]$Process
    )

    begin {

        $InputArray = @()

        # fetch the last statement in the scriptblock
        $EndBlock = $Process.Ast.EndBlock
        $LastStatement = $EndBlock.Statements[-1].Extent.Text.Trim()

        # check if the last statement is `$_`
        if($LastStatement -ne '$_'){
            # if not, add it
            $Process = [scriptblock]::Create('{0};$_' -f $Process.ToString())
        }
    }

    process {
        # collect all the input
        $InputArray += $InputObject
    }

    end {
        # pipe input to foreach-object with the new scriptblock
        $InputArray | ForEach-Object -Process $Process
    }
}

现在,用户可以:

代码语言:javascript
复制
Get-Objects | Add-PsItem {$_.testString = "newValue"} | Set-Objects

ValueFromRemainingArguments属性还允许用户将输入作为无界参数值提供:

代码语言:javascript
复制
PS C:\> Add-PsItem { $_ *= 10 } 1 2 3
10
20
30

如果用户不习惯使用管道,这可能会有所帮助。

票数 21
EN

Stack Overflow用户

发布于 2015-12-09 09:23:38

这里有一种更通用的方法,可以说更容易理解,也不那么脆弱:

代码语言:javascript
复制
#  $dataSource  would be get-object in the OP
#  $dataUpdater is the script the user supplies to modify properties
#  $dataSink    would be set-object in the OP
function Update-Data {
  param(
    [scriptblock]   $dataSource,
    [scriptblock]   $dataUpdater,
    [scriptblock]   $dataSink
  )

  & $dataSource |
  % { 
      $updaterOutput = & $dataUpdater
      # This "if" allows $dataUpdater to create an entirely new object, or
      # modify the properties of an existing object
      if ($updaterOutput -eq $null) {
        $_
      } else {
        $updaterOutput
      }
    } |
  % $dataSink
}

下面是几个使用的例子。第一个示例不适用于OP,但它用于创建适用的数据集(一组具有属性的对象)。

代码语言:javascript
复制
#      Use updata-data to create a set of data with properties
#
$theDataSource = @() # will be filled in by first update-data
update-data {

    # data source
    0..4 
  } { 

    # data updater: creates a new object with properties
    New-Object psobject | 
    # add-member uses hash table created on the fly to add properties
    # to a psobject
    add-member -passthru -NotePropertyMembers @{
               room = @('living','dining','kitchen','bed')[$_];
               size = @(320,     200,     250,      424  )[$_]}
  } {

    # data sink
    $global:theDataSource += $_
  }

$theDataSource  | ft -AutoSize


#      Now use updata-data to modify properties in data set
#      this $dataUpdater updates the 'size' property 
#

$theDataSink = @()
update-data { $theDataSource } { $_.size *= 2} { $global:theDataSink += $_}
$theDataSink | ft -AutoSize

然后输出:

代码语言:javascript
复制
room    size
----    ----
living   320
dining   200
kitchen  250
bed      424

room    size
----    ----
living   640
dining   400
kitchen  500
bed      848

如前所述,更新-数据依赖于“流”数据源和接收器。没有关于第一个或第15个元素是否正在被修改的概念。或者,如果数据源使用键(而不是索引)访问每个元素,则数据接收器将无法访问该键。要处理这种情况,可以将“上下文”(例如索引或键)与数据项一起传递到管道中。$dataUpdater不需要(必然)查看上下文。下面是一个经过修改的版本,并添加了这个概念:

代码语言:javascript
复制
# $dataSource and $dataSink scripts need to be changed to output/input an
# object that contains both the object to modify, as well as the context.
# To keep it simple, $dataSource will output an array with two elements:
# the value and the context. And $dataSink will accept an array (via $_) 
# containing the value and the context.
function Update-Data {
  param(
    [scriptblock]   $dataSource,
    [scriptblock]   $dataUpdater,
    [scriptblock]   $dataSink
  )

  %  $dataSource |
  % { 
      $saved_ = $_
      # Set $_ to the data object
      $_ = $_[0]

      $updaterOutput = & $dataUpdater
      if ($updaterOutput -eq $null) { $updaterOutput = $_}

      $_ = $updaterOutput, $saved_[1]
    } |
  % $dataSink
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34166023

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档