如何在powershell中递归附加到文件名? [英] How to recursively append to file name in powershell?
问题描述
我在文件夹/它们的子文件夹中有多个 .txt 文件.
我想在他们的文件名后附加 _old.
我试过了:
Get-ChildItem -Recurse |Rename-Item -NewName {$_.name -replace '.txt','_old.txt' }
结果:
- 某些文件已正确更新
- 某些文件更新不正确 - 它们得到 _old 两次 - 示例:
.._old_old.txt
- 有几个错误:
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-Item
在 Get-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-ChildItem
在 PowerShell [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:
- Some files get updated correctly
- Some files get updated incorrectly - they get _old twice - example:
.._old_old.txt
- 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$&'
- An alternative is to stick with
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屋!