【问题标题】:Prevent PowerShell ForEach-Object to flatten the list?防止 PowerShell ForEach-Object 展平列表?
【发布时间】:2022-03-28 10:37:36
【问题描述】:

如果有两个以上的项目,以下代码将打印计数。 .Split(',') 被调用了两次。

'a,b,c', 'x,y', '1,2,3' |
Where-Object { $_.Split(',').Count -gt 2 } |
ForEach-Object { $x = $_.Split(','); $x.Count }

以下代码尝试调用一次.Split(',')。它没有任何输出。

'a,b,c', 'x,y', '1,2,3' |
ForEach-Object { @($_.Split(',')) } | # got a single list instead of `list of list of string`
Where-Object { $_.Count -gt 2 } |
ForEach-Object { $_.Count }

但是,ForEach-Objectlist of list 展平以列出。这是一种防止扁平化的方法吗?

【问题讨论】:

  • 您可以尝试使用一元逗号运算符。 ForEach-Object { ,($_.Split(',')) }

标签: powershell


【解决方案1】:

您可以利用Where-ObjectForEach-Object 在同一范围内(即调用者的范围)运行传递给它们的脚本块({ ... })这一事实:

'a,b,c', 'x,y', '1,2,3', 'a,b,c,d' |
  Where-Object { ($count = $_.Split(',').Count) -gt 2 } |
    ForEach-Object { $count }

也就是说,分配给Where-Object 脚本块中的$count 变量也可以在ForEach-Object 脚本块中访问,逐个输入对象。

也就是说,您可以单独使用 ForEach-Object 做所有您需要的事情:

'a,b,c', 'x,y', '1,2,3', 'a,b,c,d' |
  ForEach-Object { $count = ($_ -split ',').Count; if ($count -gt 2) { $count } }

请注意,我已从 .Split() 方法 切换到使用 PowerShell 的 more flexible -split 运算符


至于你尝试了什么

将数组(可枚举)输出到管道会导致其元素一个接一个地发送,而不是作为整个数组发送 - 有关背景信息,请参阅this answer

避免这种情况的最简单方法,即将数组作为一个整体发送,是将这样的数组包装在辅助单元素中包装数组,使用,的一元形式,数组构造运算符:, $_.Split(',')

请注意,@(...) 中包含一个命令执行相同的包装,因为@(...)构造 一个数组;松散地说,它只是确保输出是一个数组,所以如果输入已经一个数组 - 如你的情况 - @(...) 是 -粗略地说 - 一个(昂贵的)no-op - 请参阅this answer 的底部了解详细信息。

【讨论】:

  • Re: “输出一个数组 ... 会导致它的元素一个接一个地发送,而不是整个数组。” 澄清一下可能很有用:它并不完全只输出一个'array',但输出其中一些项是数组的数组'逐项发送'适用于items - 作为数组的项目被“展平”到外部数组上。扁平化发生在ForEach-Object { @($_.Split(',')) },因为它是这样一个“其项目是数组的数组”。 An SO answer here 可能会对此行为提供更多解释。
  • @SlawomirBrzezinski,在这种情况下没有嵌套数组在起作用,假设这就是你的意思,并讨论 数组扁平化 而不是 的行为枚举 最终令人困惑。在每个ForEach-Object 迭代中,$_.Split(',') 返回一个平面数组(将其包装在@(...) 中只是一个代价高昂的空操作)。在输出到管道时,数组得到枚举,因此每次迭代输出单个字符串,当在变量中捕获时变为常规(在本例中为平面)PowerShell 数组,类型为 [object[]]
  • 我看到,根据我对不同描述的建议,我没有注意上下文,即管道(我是从讨论没有管道的 cmdlet 的结果来到这里的)。我指出ForEach-Object cmdlet 产生可枚举的,但管道中的所有内容都可以 - 枚举包含所有内容(甚至$scalar | ...),因此您将项目称为“输出”是正确的。对不起!回复:“根据 ... 数组而不是枚举来讨论行为最终会令人困惑” 我仅以您使用该术语作为同义词的方式使用“数组”(在您的 “数组(enumerable)"),否则我的评论会太长;P
  • 如前所述,您的描述对于管道来说确实足够了,因为它们隐式地添加了外部枚举,但只是为了在管道上下文之外展示有关此行为的有趣事实,执行此单行:@987654349 @。它输出“字符串”,因此数组(使用,"1" 强制)变平了。我的SO answer I mentioned 如果有兴趣,可能会有用。
  • @SlawomirBrzezinski,管道也适用于不明确使用|,管道操作员:任何脚本或函数输出到管道,所以相同的规则适用于您的 GetSingleResultAsArray 函数:单元素数组 , '"1",被枚举并捕获包含该单个对象的输出流,因此捕获那个对象本身。用 数组 来概念化 PowerShell 管道只会导致混乱。
猜你喜欢
  • 2013-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多