使用Powershell根据包含父文件夹名称的公式自动批量重命名文件

问题描述 投票:0回答:3

如何使用以下代码在powershell中批量重命名文件:

$nr=1;Get-ChildItem -Filter *.jpg |
  Rename-Item -Newname {"PPPPPPP_{0:d3}.jpg" -f $global:nr++}

其中 PPPPPPP 是包含这些文件的父文件夹的名称。

预期输出:

PPPPPPP_001.jpg
PPPPPPP_002.jpg
PPPPPPP_003.jpg

文件位于 C:\USER\MAIN\BLABLABLA\PPPPPPP 文件夹中。

powershell sequential batch-rename delay-bind
3个回答
1
投票
  • 通过脚本块内的

    $_.Directory.Name
    获取父目录的名称。

  • 使用

    Get-Variable
    获取对
    调用者 
    作用域中 $nr 序列号变量的引用,因此您可以直接修改其值(通过
    .Value
    ),这比使用作用域修饰符
     更可取$global:
    (可以添加
    -Scope 1
    来显式定位 parent 范围,但这并不是绝对必要的,为了简洁起见,将其省略):

$nr = 1
Get-ChildItem -Filter *.jpg | Rename-Item -Newname {
  '{0}_{1:d3}.jpg' -f $_.Directory.Name, (Get-Variable nr).Value++
} -WhatIf

-WhatIf
预览重命名操作;一旦您确信该命令将按预期执行,请将其删除。

  • 如果您的命令在脚本文件的顶级范围中运行,则一个实用的快捷方式是使用
    $script:
    范围修饰符 来引用该范围内的变量:
$nr = 1
Get-ChildItem -Filter *.jpg | Rename-Item -Newname {
  '{0}_{1:d3}.jpg' -f $_.Directory.Name, $script:nr++
} -WhatIf
  • 与作用域无关的替代方案同样简洁 - 但更晦涩 - 是将
    $nr
    变量强制转换为
    [ref]
    ,以便您可以直接在调用者的作用域中修改其值(通过
    .Value
    )。
$nr = 1
Get-ChildItem -Filter *.jpg | Rename-Item -Newname {
  '{0}_{1:d3}.jpg' -f $_.Directory.Name, ([ref] $nr).Value++
} -WhatIf
  • 最后,另一种选择是使用辅助。 哈希表
$nr = @{ Value = 1 }
Get-ChildItem -Filter *.jpg | Rename-Item -Newname {
  '{0}_{1:d3}.jpg' -f $_.Directory.Name, $nr.Value++
} -WhatIf

以下部分解释了这些技术。


可选阅读:修改延迟绑定脚本块或计算属性中调用者的变量:

您不能在脚本块中直接使用

$nr++
来直接增加序列号的原因是:

  • 延迟绑定脚本块(例如传递给

    Rename-Item -NewName
    的脚本块)和计算属性中的脚本块在child范围中运行。

    • 将此与传递给
      Where-Object
      ForEach-Object
      的脚本块进行对比,后者直接在调用者的作用域中运行。
      目前尚不清楚这种行为差异是否是有意为之。
  • 因此,
  • 尝试修改调用者的变量会创建一个

    局部变量,该变量在每次迭代中都会超出范围,以便下一次迭代再次看到原始值:

  • 顺便说一句:建议的未来增强功能将通过引入反映当前管道对象序列号的
  • automatic

    $PSIndex变量来消除手动维护序列号的需要:请参阅

    GitHub问题# 13772
    .

  • 以计算属性为例:

PS> $nr = 1; 1..2 | Select-Object { '#' + $nr++ } '#' + $nr++ ------------- #1 #1 # !! the *caller's* $nr was NOT incremented

虽然您可以使用作用域修饰符(例如 
$global:

$script:
)显式引用父作用域中的变量,但这些是
absolute
作用域引用可能无法按预期工作:举个例子:如果您移动代码到脚本中,$global:nr不再引用使用
$nr = 1
创建的变量。

顺便说一句:通常应该避免创建

global 变量,因为它们即使在脚本退出后也会保留在当前会话中。

稳健的方法是使用

Get-Variable -Scope 1调用

来稳健地引用直接父作用域:
PS> $nr = 1; 1..2 | Select-Object { '#' + (Get-Variable -Scope 1 nr).Value++ } '#' + (Get-Variable -Scope 1 nr).Value++ ------------------------------------------ #1 #2 # OK - $nr in the caller's scope was incremented

虽然这种技术很强大,但 cmdlet 调用会带来开销,而且有点冗长,但是:

    为了简洁起见,您可以省略
  • -Scope

    参数。

    
    

  • 或者,您可以通过以下方式提高效率:
  • $nr = 1; $nrVar = Get-Variable nr 1..2 | Select-Object { '#' + $nrVar.Value++ }

    
    
  • 使用

[ref] 类型提供了更简洁的替代方案

,尽管解决方案有点
晦涩: PS> $nr = 1; 1..2 | Select-Object { '#' + ([ref] $nr).Value++ } '#' + ([ref] $nr).Value++ --------------------------- #1 #2 # OK - $nr in the caller's scope was incremented

将变量转换为 
[ref]

返回一个对象,其

.Value
属性可以访问并修改该变量的值。请注意,由于此时
$nr
并未被
 分配给 
,因此确实引用了 调用者的 $nr 变量。
如果您不介意

使用辅助。

hashtable,您可以利用哈希表是 .NET 引用类型这一事实,这意味着运行延迟绑定脚本块的子作用域会看到与调用者作用域完全相同的对象,并且因此,修改该对象的 property (条目)会在调用中持续存在: PS> $nr = @{ Value = 1 }; 1..2 | Select-Object { '#' + $nr.Value++ } '#' + $nr.Value++ --------------------------- #1 #2 # OK - $nr in the caller's scope was incremented



0
投票
编辑:由于

mklement0的提示而修改。 要获取父名称,请使用 .Directory.Name
作为格式运算符的另一个参数


$nr=1 Get-ChildItem *.jpg -file| Rename-Item -Newname {"{0}_{1:d3}.jpg" -f $_.Directory.Name,([ref]$nr).Value++} -whatIf

如果输出看起来正常,请删除 -WhatIf

这仅在没有重叠范围进行重命名时才有效,在这种情况下,您可能应该使用临时扩展名。

我的德语区域设置 Windows 上的示例输出

WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\150.jpg Ziel: A:\test\test_007.jpg". WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\151.jpg Ziel: A:\test\test_008.jpg". WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\152.jpg Ziel: A:\test\test_009.jpg". WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\153.jpg Ziel: A:\test\test_010.jpg". WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\154.jpg Ziel: A:\test\test_011.jpg". WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\155.jpg Ziel: A:\test\test_012.jpg".



0
投票

$nr=1;Get-ChildItem -Filter *.jpg | Rename-Item -Newname {"$(split-path -leaf $_.Directory)_{0:d3}.jpg" -f $global:nr++}

© www.soinside.com 2019 - 2024. All rights reserved.