无法在 PowerShell 中使用多个 -replace 语句

无法在 PowerShell 中使用多个 -replace 语句

我一直在尝试实现更一致的文件命名方案,并选择使用 PowerShell 来帮助完成这项任务。但是,出于某种原因,我无法让脚本用下划线替换多个模式。

$regex = [regex] '^\d{4}\d{2}\d{2}_'
# $special = [regex] '\. - '

# prepend with date
get-childitem -attributes !directory+!system | where-object {
    !($_.name -match $regex)
} | rename-item -newname {
    (get-date $_.creationtime -format "yyyyMMdd_") + $_.basename + $_.extension
}

# make all letters lowercase
get-childitem -attributes !directory+!system | %{
    $newFilename = ($_.basename.tolower())+($_.extension.tolower());
    move-item $_ $newFilename
}

# replace special chars w underscores
get-childitem -attributes !directory+!system | where-object {
    $_.basename.contains(" ") -or $_.basename.contains("-") -or $_.basename.contains(" ") -or $_.basename.contains(".")
} | rename-item -newname {
    $_.basename -replace "\s+-\s+", "_" -replace "-+", "_" -replace "\.+", "_" -replace "\s+", "_" + $_.extension
}

前两个块工作正常,但-replace语句被忽略,并且没有任何东西被下划线替换。我在这里和谷歌上搜索过,但找不到有效的解决办法。

我也尝试过这种变化:

$_.basename.replace(".", "_") + $_.extension;
$_.basename.replace(" ", "_") + $_.extension
...

而不是正则表达式,但当它到达“.”部分时,文件扩展名就会混乱。它只对第一个模式有效,而忽略其余模式。

答案1

对于第三个块,你可以这样做

# replace special chars w underscores
Get-ChildItem -Attributes !directory+!system | Where-Object { $_.BaseName -match '[-+._\s]' } | ForEach-Object {
    $newName = '{0}{1}' -f ($_.BaseName -replace '[-+._\s]+', '_') , $_.Extension
    $_ | Rename-Item -NewName $newName -Force -WhatIf
}

-WhatIf如果控制台输出显示正确的新文件名,则删除开关。

例子:

20190126_this is a file - name that contains + - spaces and dots.pdf

变成:

20190126_this_is_a_file_name_that_contains_spaces_and_dots.pdf

正则表达式详细信息:

[-+._\s]    Match a single character present in the list below
            One of the characters “-+._”
            A whitespace character (spaces, tabs, line breaks, etc.)
   +        Between one and unlimited times, as many times as possible, giving back as needed (greedy)


编辑

虽然上面只显示了正则表达式运算符的使用-replace,但可以通过将原来的三个循环合并为一个来简化代码:

Get-ChildItem -Attributes !directory+!system | ForEach-Object {
    # get the file's BaseName and if needed prefix the CreationTime to it
    $baseName = if ($_.BaseName -notmatch '^\d{8}_') { 
        '{0:yyyyMMdd}_{1}' -f $_.CreationTime, $_.BaseName
    }
    else {
        $_.BaseName
    }
    # do a regex -replace on the base name and combine it with the file's extension
    $newName = '{0}{1}' -f ($baseName -replace '[-+._\s]+', '_') , $_.Extension
    # do the rename using the lowercase $newName
    $_ | Rename-Item -NewName $newName.ToLower() -Force -WhatIf
}

编辑2

灵感来自基思·米勒的注释,使用脚本块作为参数,您可以在上面的代码中-NewName处理:ForEach-Object

Get-ChildItem -Attributes !directory+!system | Rename-Item -NewName {
        # get the file's BaseName and if needed prefix the CreationTime to it
        $baseName = if ($_.BaseName -notmatch '^\d{8}_') { 
            '{0:yyyyMMdd}_{1}' -f $_.CreationTime, $_.BaseName
        }
        else {
            $_.BaseName
        }
        # do a regex -replace on the base name and combine it with the file's extension
        $newName = '{0}{1}' -f ($baseName -replace '[-+._\s]+', '_') , $_.Extension
        # output the new name in lower case
        $newName.ToLower()
    }

答案2

你可以用管道输送获取子项foreach 对象循环获取每个文件对象及其属性值。使用正则表达式多匹配字符表达式堆叠一些替换运算符,并使用它来删除并替换基本文件名部分中不必要的字符。

本质上,这将用下划线替换点、连字符和一个或多个空格,然后在其顶部进行堆叠替换,将两个或多个连字符替换为一个连字符。

$regex = [regex] '^\d{8}_';
Get-ChildItem -attributes !directory+!system | ?{!($_.name -match $regex)} | %{ 
    Rename-Item -path $_.FullName -newname ((get-date $_.creationtime -format "yyyyMMdd_") + $_.basename + $_.extension).ToLower()
    };

Get-ChildItem -attributes !directory+!system | %{
    If(($_.basename + $_.extension) -cmatch "[A-Z]"){Move-Item $_ ($_.basename + $_.extension).ToLower()}
    };

Get-ChildItem -attributes !directory+!system | %{
    Rename-Item -Path $_.FullName -NewName "$(Split-Path $_.FullName)\$(($_.Basename -Replace "[.]|-|\s`+","_") -Replace "_{2,}", "_")$($_.extension)"
    };

支持资源

答案3

编辑:简化代码---

$DateFilter = '^\d{8}_'
$CharFilter = '[\.\s-]+' # 'dot'or one or more whitespace or '-'

$Standarize_Name = {
$Prefix = ''
If ($_.name -notmatch $DateFilter) {
   $Prefix = (get-date $_.creationtime -format "yyyyMMdd_")
}
'{0}{1}{2}' -f $Prefix,
              ($_.BaseName.ToLower() -replace $CharFilter, '_'),
               $_.Extension
}
Get-ChildItem -Attributes !directory+!system |
   Rename-Item -NewName $Standarize_Name

您的原始代码一切正常,只是需要一对括号。我拿走了您的改名ScriptBlock 并在一个简单的每一个

PS C:\...\keith>gi 'a b - c.d.txt' | %{$_.basename -replace "\s+-\s+", "_" -replace "-+", "_" -replace "\.+", "_" -replace "\s+", "_" + $_.extension}
The -ireplace operator allows only two elements to follow it, not 3.
At line:1 char:99
+ ... \s+-\s+", "_" -replace "-+", "_" -replace "\.+", "_" -replace "\s+",  ...
+                                                          ~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Object[]:Object[]) [], RuntimeException
    + FullyQualifiedErrorId : BadReplaceArgument

最后出错了-代替因为它被读+ $_.extension作“第三元素”。

因此解决办法是将-代替用括号括起来,然后连接$_.扩展名

# replace special chars w underscores
get-childitem -attributes !directory+!system | where-object {
    $_.basename -match '\s|-|\.')
} | rename-item -newname {
    ($_.basename -replace "\s+-\s+", "_" -replace "-+", "_" -replace "\.+", "_" -replace "\s+", "_") + $_.extension
}

相关内容