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

标签 powershell

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

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/

相关文章:

string - PowerShell 使用字符串中的变量作为参数传递

regex - 在 PowerShell 中用环境变量替换正则表达式 token

.net - [System.Net.Dns]::GetHostEntry ("ip") 返回另一台服务器的主机条目

function - 使用带有函数的管道获得不同的结果

windows - 添加名称中带有空格的Active Directory组会在PowerShell脚本中给出错误

email - Powershell发送带有二进制附件的电子邮件

function - 当仅显式返回一个参数时,Powershell函数将返回所有参数

powershell - 将参数从批处理文件传递给powershell脚本

windows - 如何使用 cacert 文件在 Powershell (Invoke-WebRequest) 中执行等效的 cUrl?

powershell - Powershell-替换领先路径信息