如何在powershell中递归附加到文件名? [英] How to recursively append to file name in powershell?

查看:64
本文介绍了如何在powershell中递归附加到文件名?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在文件夹/它们的子文件夹中有多个 .txt 文件.

我想在他们的文件名后附加 _old.

我试过了:

Get-ChildItem -Recurse |Rename-Item -NewName {$_.name -replace '.txt','_old.txt' }

结果:

  1. 某些文件已正确更新
  2. 某些文件更新不正确 - 它们得到 _old 两次 - 示例:.._old_old.txt
  3. 有几个错误:Rename-Item:源路径和目标路径必须不同.

解决方案

为了防止已重命名的文件意外重新进入文件枚举,从而多次重命名,将您的 Get-ChildItem 调用放在 () 中,分组运算符,它确保首先收集所有输出em>[1],在通过管道发送结果之前:

(Get-ChildItem -Recurse) |Rename-Item -NewName { $_.name -replace '\.txt$', '_old.txt' }

请注意,我使用了 \.txt$ 作为正则表达式[2],以确保只有文字 .(\.) 后跟字符串 txt 在文件名的end ($) 匹配,所以以防止误报(例如,名为 Atxt.csv 的文件或什至名为 AtxtB目录 会意外匹配您的原始正则表达式).

注意:收集所有 Get-ChildItem 输出的需要首先源于 PowerShell 管道的基本工作方式:对象(默认情况下)被发送到管道一个接一个,并由接收命令在接收它们时进行处理.这意味着,如果 Get-ChildItem 周围没有 (...)Rename-ItemGet-ChildItem<之前开始重命名文件/code> 已完成枚举文件,这会导致问题.如需详细了解 PowerShell 管道的工作原理,请参阅此答案.
感谢 Matthew 建议包含此信息.

但是,我建议按如下方式优化您的命令:

(Get-ChildItem -Recurse -File -Filter *.txt) |Rename-Item -NewName { $_.BaseName + '_old' + $_.Extension }

  • -File 将输出限制为文件(也不会返回目录).

  • -Filter 是将结果限制为给定通配符模式的最快方法.

  • $_.BaseName + '_old' + $_.Extension 通过文件名的子组件使用简单的字符串连接.

    • 另一种方法是坚持使用 -replace:
      $_.Name -replace '\.[^.]+$', '_old$&'

请注意,如果您想重复运行此操作并需要排除在前一次运行中重命名的文件,请将 -Exclude *_old.txt 添加到 Get-ChildItem 调用.

<小时>

[1] 由于 Get-ChildItemPowerShell [Core] 6+ 中的实现方式发生了变化(它现在在内部对结果进行排序,总是需要首先收集它们),(...) 外壳不再严格必需,但这可以被视为实现细节,因此为了概念清晰,最好继续使用(...).

[2] PowerShell 的 -replace 运算符正则表达式进行操作(正则表达式);它不像 [string] 类型的 .Replace() 方法那样执行 literal 子字符串搜索.

I have multiple .txt files in folders/their sub-folders.

I want to append _old to their file names.

I tried:

Get-ChildItem -Recurse | Rename-Item -NewName {$_.name -replace '.txt','_old.txt' }

This results in:

  1. Some files get updated correctly
  2. Some files get updated incorrectly - they get _old twice - example: .._old_old.txt
  3. There are few errors: Rename-Item : Source and destination path must be different.

解决方案

To prevent already renamed files from accidentally reentering the file enumeration and therefore getting renamed multiple times, enclose your Get-ChildItem call in (), the grouping operator, which ensures that all output is collected first[1], before sending the results through the pipeline:

(Get-ChildItem -Recurse) |
  Rename-Item -NewName { $_.name -replace '\.txt$', '_old.txt' }

Note that I've used \.txt$ as the regex[2], so as to ensure that only a literal . (\.) followed by string txt at the end ($) of the file name is matched, so as to prevent false positives (e.g., a file named Atxt.csv or even a directory named AtxtB would accidentally match your original regex).

Note: The need to collect all Get-ChildItem output first arises from how the PowerShell pipeline fundamentally works: objects are (by default) sent to the pipeline one by one, and processed by a receiving command as they're being received. This means that, without (...) around Get-ChildItem, Rename-Item starts renaming files before Get-ChildItem has finished enumerating files, which causes problems. See this answer for more information about how the PowerShell pipeline works.
Tip of the hat to Matthew for suggesting inclusion of this information.

However, I suggest optimizing your command as follows:

(Get-ChildItem -Recurse -File -Filter *.txt) | 
  Rename-Item -NewName { $_.BaseName + '_old' + $_.Extension }

  • -File limits the the output to files (doesn't also return directories).

  • -Filter is the fastest way to limit results to a given wildcard pattern.

  • $_.BaseName + '_old' + $_.Extension uses simple string concatenation via the sub-components of a file name.

    • An alternative is to stick with -replace:
      $_.Name -replace '\.[^.]+$', '_old$&'

Note that if you wanted to run this repeatedly and needed to exclude files renamed in a previous run, add -Exclude *_old.txt to the Get-ChildItem call.


[1] Due to a change in how Get-ChildItem is implemented in PowerShell [Core] 6+ (it now internally sorts the results, which invariably requires collecting them all first), the (...) enclosure is no longer strictly necessary, but this could be considered an implementation detail, so for conceptual clarity it's better to continue to use (...).

[2] PowerShell's -replace operator operates on regexes (regular expressions); it doesn't perform literal substring searches the way that the [string] type's .Replace() method does.

这篇关于如何在powershell中递归附加到文件名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆