使用PowerShell重命名文件名和文件中的内容以尊重原始案例

bf1o4zei  于 2023-10-18  发布在  Shell
关注(0)|答案(2)|浏览(101)

关于使用Powershell重命名文件和文件中的内容的问题,请遵循解决方案here
使用下面的脚本,文件中的所有文件名和引用都将重命名。替换不区分大小写,即无论是否出现“uvw”、“UVW”、“UVw”等,替换都是“XYZ”。是否有可能尊重原件的情况,并重新命名为“真实原件”,即“uvw”->“xyz”,“UVW”->“XYZ”,“UVW”->“Xyz”(默认情况下“abc_123”也应该是“def_123”而不是“DEF_123”)?

$filePath = "C:\root_folder"
$include = '*.txt', '*.xml' # adapt as needed
Get-ChildItem -File $filePath -Recurse -Include $include | 
 Rename-Item -WhatIf -PassThru -NewName { $_.Name -replace 'UVW', 'XYZ' } |
 ForEach-Object {
 ($_ | Get-Content -Raw) -replace 'ABC_123', 'DEF_123' |
   Set-Content -NoNewLine -LiteralPath $_.FullName
}
yqhsw0fo

yqhsw0fo1#

Santiago's helpful answer的另一种方法,也依赖于为每个匹配调用的匹配评估器脚本块:
总的想法是:

  • 循环遍历匹配文本和替换文本共享的所有字符
  • 使用System.Text.StringBuilder示例逐字符构建替换文本的大小写匹配版本。
  • 替换字符串中的任何其他字符(在匹配文本中没有对应字符的字符)将按原样保留。

两种解决方案均低于预期输出<XyZ>
PowerShell (Core) 7+解决方案:

# Sample input string
$inputString = '<UvW>'
# Sample search pattern.
$searchPattern = 'uvw'
# Sample replacement text.
$replaceWith = 'xyz'

# A string builder to make constructing the case-matched replacement string
# more efficient.
$caseMatchedReplaceWith = [System.Text.StringBuilder]::new($replaceWith.Length)

$inputString -replace $searchPattern, {  
  $matchedText = $_.Value
  $numCharsToMatch = [Math]::Min($replaceWith.Length, $matchedText.Length)
  $null = $caseMatchedReplaceWith.Clear()
  foreach ($i in 0..($numCharsToMatch-1)) {
    $replacementChar = $replaceWith[$i]
    if ([char]::IsUpper($matchedText[$i]) -and -not [char]::IsUpper($replacementChar)) {
      $replacementChar = [char]::ToUpper($replacementChar)
    } 
    $null = $caseMatchedReplaceWith.Append($replacementChar)
  }
  $caseMatchedReplaceWith.ToString() + $replaceWith.Substring($numCharsToMatch)
}
  • Windows PowerShell* 解决方案(不支持使用脚本块作为-replace的替换操作数):
# Sample input string
$inputString = '<UvW>'
# Sample search pattern.
$searchPattern = 'uvw'
# Sample replacement text.
$replaceWith = 'xyz'

# A string builder to make constructing the case-matched replacement string
# more efficient.
$caseMatchedReplaceWith = [System.Text.StringBuilder]::new($replaceWith.Length)

[regex]::Replace(
  $inputString,
  $searchPattern, 
  {  
    $matchedText = $args[0].Value
    $numCharsToMatch = [Math]::Min($replaceWith.Length, $matchedText.Length)
    $null = $caseMatchedReplaceWith.Clear()
    foreach ($i in 0..($numCharsToMatch-1)) {
      $replacementChar = $replaceWith[$i]
      if ([char]::IsUpper($matchedText[$i]) -and -not [char]::IsUpper($replacementChar)) {
        $replacementChar = [char]::ToUpper($replacementChar)
      } 
      $null = $caseMatchedReplaceWith.Append($replacementChar)
    }
    $caseMatchedReplaceWith.ToString() + $replaceWith.Substring($numCharsToMatch)
  },
  'IgnoreCase'
)
wpcxdonn

wpcxdonn2#

所以,这是令人难以置信的低效,但我没有看到一种方法,你需要使用一个匹配评估器和一个哈希表来Map匹配的字符与它们的替换字符。
代码看起来会有所不同,这取决于您是在PowerShell 7+上使用脚本块进行替换,还是在Windows PowerShell 5.1上使用脚本块进行替换,在Windows PowerShell 5.1上您需要调用Regex.Replace API来定位MatchEvaluator重载之一。

  • PowerShell 7+
$evaluator = {
    # enumerate each character from the matched value
    # `.Value` in this context from the examples would be `uvw`, `UVW` and `Uvw`
    $result = foreach ($char in $_.Value.GetEnumerator()) {
        # here we get the replacement character
        # i.e.:
        #  - if `$char` is `u` then `$value` is `x`
        #  - if `$char` is `v` then `$value` is `y`
        $value = $map[$char.ToString()]
        # check if the enumerated character is uppercase
        if ([char]::IsUpper($char)) {
            # then, we need to output the uppercase replacement char too
            $value.ToUpper()
            # and go to the next char
            continue
        }
        
        # else, just output the char as-is (lowercase)
        $value
    }

    # lastly, after all matched characters are processed,
    # create a new string from the char array
    [string]::new($result)
}

$map = @{
    u = 'x'
    v = 'y'
    w = 'z'
}

'foo uvw bar' -replace 'uvw', $evaluator # Outputs: foo xyz bar
'foo UVW bar' -replace 'uvw', $evaluator # Outputs: foo XYZ bar
'foo Uvw bar' -replace 'uvw', $evaluator # Outputs: foo Xyz bar
  • Windows PowerShell 5.1:
$evaluator = {
    $result = foreach ($char in $args[0].Value.GetEnumerator()) {
        $value = $map[$char.ToString()]
        if ([char]::IsUpper($char)) {
            $value.ToUpper()
            continue
        }

        $value
    }

    [string]::new($result)
}

$map = @{
    u = 'x'
    v = 'y'
    w = 'z'
}

$re = [regex]::new('uvw', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
$re.Replace('foo uvw bar', $evaluator) # Outputs: foo xyz bar
$re.Replace('foo UVW bar', $evaluator) # Outputs: foo XYZ bar
$re.Replace('foo Uvw bar', $evaluator) # Outputs: foo Xyz bar

相关问题