PowerShell:`$matches` 是否保证与管道变量同步执行管道? [英] PowerShell: Is `$matches` guaranteed to carry down the pipeline in sync with the pipeline variable?

查看:54
本文介绍了PowerShell:`$matches` 是否保证与管道变量同步执行管道?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

First, make some example files:

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 中编写它来完成这两项工作,并且运行良好:

What I want to do is match the year with a regex capture group, then reference the match with $matches[1] and use it. I can write this to do both in one scriptblock, in one cmdlet, and it works fine:

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

我也可以写这个来在一个脚本块中进行匹配,然后在另一个 cmdlet 的脚本块中引用 $matches:

I can also write this to do the match in one scriptblock, and then reference $matches in another cmdlet's scriptblock later on:

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
}

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

Which has the same output and it appears to work fine. But is it guaranteed to work, or is it undefined and a coincidence?

可以'example 2012.txt' 匹配,然后缓冲.'example 2013.txt' 匹配,然后缓冲.<代码>|foreach 开始处理 'example 2012.txt'$matches 已经用 2013 更新了同步?

Could 'example 2012.txt' get matched, then buffered. 'example 2013.txt' gets matched, then buffered. | foreach gets to work on 'example 2012.txt' but $matches has already been updated with 2013 and they're out of sync?

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

I can't make them fall out of sync - but I could still be relying on undefined behaviour.

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

(FWIW, I prefer the first approach for clarity and readability as well).

推荐答案

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

There is no synchronization going on, per se. The second example works because of the way the pipeline works. As each single object gets passed along by satisfying the condition in Where-Object, the -Process block in ForEach-Object immediately processes it, so $Matches hasn't yet been overwritten from any other -match operation.

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

If you were to do something that causes the pipeline to gather objects before passing them on, like sorting, you would be in trouble:

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 个对象,但它们都将是最后一个匹配项.

For example, the above should fail, outputting n objects, but they will all be the very last match.

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

So it's prudent not to rely on that, because it obscures the danger. Someone else (or you a few months later) may not think anything of inserting a sort and then be very confused by the result.

正如 TheMadTechnician 在评论中指出的那样,位置会改变一些事情.将排序放在引用 $Matches 的部分之后(在 foreach 中),或者在使用 where 过滤之前,它仍然可以工作正如预期的那样.

As TheMadTechnician pointed out in the comments, the placement changes things. Put the sort after the part where you reference $Matches (in the foreach), or before you filter with where, and it will still work as expected.

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

I think that drives home the point that it should be avoided, as it's fairly unclear. If the code changes in parts of the pipeline you don't control, then the behavior may end up being different, unexpectedly.

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

I like to throw in some verbose output to demonstrate this sometimes:

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 就会立即获取它.在 sorted 中,您可以看到所有 where 先发生,然后 foreach 获取其中任何一个.

The difference you'll see is that in the original, as soon as where "clears" an object, foreach gets it right away. In the sorted, you can see all of the wheres happening first, before foreach gets any of them.

sort 没有任何详细的输出,所以我没有费心这样称呼它,但本质上它的 Process {} 块只是收集所有对象,所以它可以比较(排序!)它们,然后在 End {} 块中将它们吐出.

sort doesn't have any verbose output so I didn't bother calling it that way, but essentially its Process {} block just collects all of objects so it can compare (sort!) them, then spits them out in the End {} block.

首先,这是一个模拟 Sort-Object 的对象集合的函数(它实际上并不对它们进行排序或做任何事情):

First, here's a function that mocks Sort-Object's collection of objects (it doesn't actually sort them or do anything):

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
    }
}

然后,我们可以在前面的示例中使用它,并在最后进行一些睡眠:

Then, we can use that with the previous example and some sleep at the end:

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` 是否保证与管道变量同步执行管道?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆