繁体   English   中英

使用 powershell 选择具有多个条件的字符串

[英]select-string with multiple conditions with powershell

我正在寻找一种方法来在文件中找到 2 行不同的行,并且只有当这 2 行存在时,我才需要执行任务。 到目前为止,这是我的代码

$folderPath = c:\test
$files = Get-ChildItem $Folderpath -Filter *.txt
$find = 'stringA'
$find2 = 'StringB'
$replace = 'something to replace with string b'
if ($files.Length -gt 0  ) {
    $files |
    select -ExpandProperty fullname |
    foreach {
        If(Select-String -Path $_ -pattern $find , $find2 -quiet )
        {
            (Get-Content $_) |
            ForEach-Object {$_ -replace $find2, $replace } |
            Set-Content $_
            write-host "File Changed : " $_    
        } 
    }
}
else {
    write-host "no files changed"
}

目前,如果我运行一次它会更改文件但如果我再次运行它也会通知我它更改了相同的文件而不是 output “没有文件更改”

有没有更简单的方法来实现它? 谢谢

Select-String cmdlet 选择与提供给它的任何模式匹配的行。 这意味着以下文件包含匹配项:

PS> Get-Content file.txt
This file contains only stringA

PS> Select-String -Pattern 'stringA', 'stringB' -Path file.txt
file.txt:1:This file contains only stringA

-Quiet标志传递给Select-String将产生 boolean 结果而不是匹配列表。 即使只存在一种模式,结果也是$True

PS> Get-Content file.txt
This file contains only stringA

PS> Select-String -Pattern 'stringA', 'stringB' -Path file.txt -Quiet
True

在您的情况下, Select-String 选择包含'stringA''stringB'所有文件,然后替换这些文件中的所有'stringB'实例。 (请注意,替换也会在您不想更改的文件中执行)

即使在替换之后,仅包含'stringA'文件仍然存在:这些文件在您第二次运行时被您的脚本捕获并报告。

一种解决方案是通过-and运算符连接两个单独的条件:

If (
    (Select-String -Path $_ -Pattern 'stringA' -Quiet) -and
    (Select-String -Path $_ -Pattern 'stringB' -Quiet)
)

在此之后,脚本应该按预期工作,除了它不会正确报告"no files changed"

如果您修复缩进,您会发现最后的 else 子句实际上检查文件夹中是否没有.txt文件:

$files = Get-ChildItem $Folderpath -Filter *.txt
...
if ($files.length -gt 0) {
    ...
} else {
    # will only display when there are no text files in the folder!
    Write-Host "no files changed"
}

解决这个问题的方法是有一个单独的计数器变量,每次找到匹配项时都会递增。 然后在最后,检查这个计数器是否为 0 并相应地调用Write-Host

$counter = 0
...
foreach {
    if ((Select-String ...) ...) {
        ...
        $counter += 1
    }
}
if ($counter -eq 0) {
    Write-Host "no files changed"
}

为了补充equatorialsnowfall 的有用答案,它很好地解释了您的方法的问题,并提供了一种简化的、可能更有效的解决方案:

$folderPath = c:\test
$searchStrings = 'stringA', 'stringB'
$replace = 'something to replace string B with'
$countModified = 0


Get-ChildItem $Folderpath -Filter *.txt | ForEach-Object {
  if (
    (
      ($_ | Select-String -Pattern $searchStrings).Pattern | Select-Object -Unique
    ).Count -eq $searchStrings.Count
  ) {
    ($_ | Get-Content -Raw) -replace $searchStrings[1], $replace |
      Set-Content $_.FullName
    ++$countModified
    write-host "File Changed: " $_    
  }
}

if ($countModified -eq 0) {
  Write-Host "no files changed"
}
  • 单个Select-String调用用于确定所有模式是否匹配(解决方案可扩展到任意数量的模式):

    • 每个Microsoft.PowerShell.Commands.MatchInfo output object 都有一个.Pattern属性,指示传递给-Pattern的模式在给定行上匹配。

    • 如果在使用Select-Object -Unique删除重复项后,与匹配行关联的模式数与输入模式数相同,则可以假设所有输入模式都匹配(至少一次)。

  • 使用Get-Content-Raw开关将每个匹配文件作为一个整体读取,因此每个文件只执行一次-replace操作比逐行处理要快得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM