【问题标题】:Issue attempting to insert character into filename尝试将字符插入文件名的问题
【发布时间】:2020-07-16 09:36:02
【问题描述】:

我想以 yyyymmdd_SequenceNumber 格式重命名特定文件夹中的所有 .jpg 文件,以便在 yyyy 和 mm 之后包含下划线。因此,例如,名为 20200712_123456.jpg 的文件将重命名为 2020_07_12_123456.jpg。应该适用于文件夹中的所有 .jpg 文件,最好也适用于所有 .mp4 文件。以下代码几乎可以工作,只是第一个 .jpg 文件名更改为 2020__07_12_123456.jpg(注意双下划线)。所有其他文件都被重命名了。

Get-ChildItem -Path 'C:\test' -Filter '*.jpg' | 
  ForEach-Object { $_ | Rename-Item -NewName {$_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension}}

奇怪的是下面的代码确实有效,但我读过一些文章说使用 -Include 的结果可能是不可预测的:

Get-ChildItem -Path 'C:\test\*' -Include *.jpg, *.mp4 | 
  ForEach-Object { $_ | Rename-Item -NewName {$_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension}}

我更像是一个批处理脚本人,但我正在尝试使用 Powershell。 提前感谢您的帮助。

【问题讨论】:

  • 您使用的是-NewName { ... },而不是-NewName "$( ... )"-NewName ( ... )。那将传递一个ScriptBlock,而不是String,给那个参数。我以前从未见过这种情况,并且会质疑这是否有效,但是……显然它几乎可以。
  • 第一个文件在重命名之前的位置 4 是否已经有下划线?
  • @BACON 是延迟脚本绑定,它应该适用于任何接受管道输入的参数。
  • @AdminOfThings 是的,刚刚在about_Script_Blocks 中找到了。不过,它似乎在这里没有提供任何优势,是吗?如果我理解的话,它将允许将Get-ChildItem 直接通过管道传输到Rename-Item,但在ForEach-Object 内部它与"$( ... )"( ... ) 相同。
  • 对我来说,在 PowerShell 5.1 中,第一个文件 20200712_123456.jpg 被重命名为 2020__0_7_12_123456.jpg两个 额外的下划线),而在 7.0.2 中它正确地变为 @987654337 @。如果我更改为Get-ChildItem ... | ForEach-Object { $_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension },它将在 5.1 上输出正确的文件名。 Get-ChildItem ... | Rename-Item -NewName {$_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension} 两个版本的行为相同:5.1 上有额外的下划线,但 7.0.2 没有。

标签: powershell


【解决方案1】:

我创建了一个C:\test 目录,其中包含文件20200712_123456.jpg20200712_123457.jpg。将-PassThru 传递给Rename-Item 说明了正在发生的事情......

PS> Get-ChildItem -Path 'C:\test' -Filter '*.jpg' | % { $_ | Rename-Item -NewName {$_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension} -PassThru }


    Directory: C:\test


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         7/15/2020   7:05 PM              0 2020_07_12_123456.jpg
-a----         7/15/2020   7:05 PM              0 2020_07_12_123457.jpg
-a----         7/15/2020   7:05 PM              0 2020__0_7_12_123456.jpg

重命名两个文件会产生三个FileInfo对象!请注意,将管道简化为...时的结果是相同的。

Get-ChildItem -Path 'C:\test' -Filter '*.jpg' |
   Rename-Item -NewName {$_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension}

我认为Get-ChildItem枚举目录时会发生什么...

  • 遇到20200712_123456.jpg 并将其重命名为2020_07_12_123456.jpg
  • 遇到20200712_123457.jpg 并将其重命名为2020_07_12_123457.jpg
  • 因为2020_07_12_123456.jpg(以前的20200712_123456.jpg)是在20200712_123457.jpg之后插入的,所以它再次遇到它并应用第二遍重命名为2020__0_7_12_123456.jpg

为什么这种重命名跳跃的游戏不能无限期地进行?我还不知道。

将目录枚举 (Get-ChildItem) 与目录列表 (Rename-Item) 的修改解耦会为我产生预期的结果...

PS> $filesToRename = Get-ChildItem -Path 'C:\test' -Filter '*.jpg'
PS> $filesToRename | ForEach-Object { $_ | Rename-Item -NewName {$_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension} -PassThru }


    Directory: C:\test


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         7/15/2020   7:05 PM              0 2020_07_12_123456.jpg
-a----         7/15/2020   7:05 PM              0 2020_07_12_123457.jpg

我确实观察到原始管道在 PowerShell 7.0.2 中按预期工作,因此这似乎是自 5.1 以来已解决的 Get-ChildItem 问题。 this answer 底部的注释确认它是另一个更改的副作用...

...[显式缓冲完整目录内容]技术上在 PowerShell [Core] 6+ 中不再需要(在 Windows PowerShell 中它必要的 (5.1-)),因为Get-ChildItemalways internally collects info about all files up front, across platforms 的方式实现,因为它按名称对它们进行排序,这在本质上只有在收集所有名称之后才有可能。

在使用之前缓冲Get-ChildItem 的结果并不理想,因此执行此下划线插入的另一种方法是attempt to parse,成功后,reformat 文件名...

PS> Get-ChildItem -Path 'C:\test' -Filter '*.jpg' |
    ForEach-Object {
        $timeFromName = [DateTime]::MinValue
        if ([DateTime]::TryParseExact($_.BaseName, 'yyyyMMdd_hhmmss', $null, [Globalization.DateTimeStyles]::None, [Ref] $timeFromName))
        {
            $_ | Rename-Item -NewName {$timeFromName.ToString('yyyy_MM_dd_hhmmss') + $_.Extension} -PassThru
        }
    }


    Directory: C:\test


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         7/15/2020   7:05 PM              0 2020_07_12_123456.jpg
-a----         7/15/2020   7:05 PM              0 2020_07_12_123457.jpg

这不仅可以防止上述双管道重命名,还可以让您随时重新运行脚本,并且只会重命名与原始格式完全匹配的文件.

【讨论】:

  • GCI 通过WhereGet-ChildItem C:\Test -Filter '*.jpg' | Where{$_.Name -match '^\d{8}'} 这样的正则表达式匹配来避免循环效果不是更简单吗?
  • 代替我上一个代码sn-p?是的,这也可以。
【解决方案2】:

我会用这个:

Get-ChildItem -Path 'C:\test\*' -Include *.jpg, *.mp4 | 
  ForEach-Object { 
    $NewName = $_.BaseName.insert(4,'_').insert(7,'_') + $_.Extension
    Rename-Item -Path "$_.Fullname" -Newname "$Newname"
}

【讨论】:

    猜你喜欢
    • 2016-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    相关资源
    最近更新 更多