gpt4 book ai didi

PowerShell: `$matches` 是否保证与管道变量同步执行管道?

转载 作者:行者123 更新时间:2023-12-04 17:07:42 25 4
gpt4 key购买 nike

首先,制作一些示例文件:

2010..2015 | % { "" | Set-Content "example $_.txt" }

#example 2010.txt
#example 2011.txt
#example 2012.txt
#example 2013.txt
#example 2014.txt
#example 2015.txt

我想要做的是将年份与正则表达式捕获组匹配,然后使用 $matches[1] 引用匹配项并使用它。我可以在一个脚本块和一个 cmdlet 中编写它来完成这两项工作,并且它工作正常:
gci *.txt | foreach { 

if ($_ -match '(\d+)') # regex match the year
{ # on the current loop variable
$matches[1] # and use the capture group immediately
}

}
#2010
#2011
#.. etc

我也可以写这个来在一个脚本块中进行匹配,然后引用 $matches稍后在另一个 cmdlet 的脚本块中:
gci *.txt | where { 

$_ -match '(\d+)' # regex match here, in the Where scriptblock

} | foreach { # pipeline!

$matches[1] # use $matches which was set in the previous
# scriptblock, in a different cmdlet
}

它具有相同的输出,并且似乎工作正常。但它是否保证有效,还是未定义和巧合?

可以 'example 2012.txt'匹配,然后缓冲。 'example 2013.txt'匹配,然后缓冲。 | foreach开始工作 'example 2012.txt'但是 $matches已经更新为 2013他们不同步?

我不能让它们不同步 - 但我仍然可以依赖未定义的行为。

(FWIW,为了清晰和可读性,我更喜欢第一种方法)。

最佳答案

本身没有同步。第二个示例之所以有效,是因为管道的工作方式。当每个单个对象通过满足 Where-Object 中的条件而被传递时, -Process阻止 ForEach-Object立即处理它,所以 $Matches尚未被其他任何地方覆盖 -match手术。

如果你要做一些事情导致管道在传递对象之前收集对象,比如排序,你会遇到麻烦:

gci *.txt | where { 

$_ -match '(\d+)' # regex match here, in the Where scriptblock

} | sort | foreach { # pipeline!

$matches[1] # use $matches which was set in the previous
# scriptblock, in a different cmdlet
}

例如,上面应该失败,输出 n 个对象,但它们都将是最后一个匹配项。

所以谨慎的做法是不要依赖它,因为它掩盖了危险。其他人(或几个月后的您)可能不会想到要插入 sort然后对结果感到非常困惑。

TheMadTechnician在评论中指出,位置会改变事情。将排序放在您引用的部分之后 $Matches (在 foreach 中),或在使用 where 过滤之前,它仍然会按预期工作。

我认为这说明应该避免它,因为它相当不清楚。如果代码在您无法控制的管道部分发生更改,则行为最终可能会出乎意料地不同。

有时我喜欢加入一些详细的输出来演示这一点:

原来的
gci *.txt | where { 
"Where-Object: $_" | Write-Verbose -Verbose
$_ -match '(\d+)' # regex match here, in the Where scriptblock

} | foreach { # pipeline!
"ForEach-Object: $_" | Write-Verbose -Verbose
$matches[1] # use $matches which was set in the previous
# scriptblock, in a different cmdlet
}

已排序
gci *.txt | where { 
"Where-Object: $_" | Write-Verbose -Verbose
$_ -match '(\d+)' # regex match here, in the Where scriptblock

} | sort | foreach { # pipeline!
"ForEach-Object: $_" | Write-Verbose -Verbose
$matches[1] # use $matches which was set in the previous
# scriptblock, in a different cmdlet
}

您将看到的不同之处在于,在原始版本中,只要 where “清除”一个对象, foreach马上得到它。在排序后,可以看到所有的 where s 首先发生,在 foreach 之前得到其中任何一个。
sort没有任何详细的输出,所以我没有费心这样称呼它,但本质上是它的 Process {}块只是收集所有对象,以便它可以比较(排序!)它们,然后将它们吐出 End {}堵塞。

更多例子

首先,这是一个模拟 Sort-Object 的函数的对象集合(它实际上并不对它们进行排序或做任何事情):
function mocksort {
[CmdletBinding()]
param(
[Parameter(
ValueFromPipeline
)]
[Object]
$O
)

Begin {
Write-Verbose "Begin (mocksort)"

$objects = @()
}

Process {
Write-Verbose "Process (mocksort): $O (nothing passed, collecting...)"

$objects += $O
}

End {
Write-Verbose "End (mocksort): returning objects"

$objects
}
}

然后,我们可以在前面的示例中使用它,并在最后进行一些 sleep :
gci *.txt | where { 
"Where-Object: $_" | Write-Verbose -Verbose
$_ -match '(\d+)' # regex match here, in the Where scriptblock

} | mocksort -Verbose | foreach { # pipeline!
"ForEach-Object: $_" | Write-Verbose -Verbose
$matches[1] # use $matches which was set in the previous
# scriptblock, in a different cmdlet
} | % { sleep -milli 500 ; $_ }

关于PowerShell: `$matches` 是否保证与管道变量同步执行管道?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40920447/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com