How to recursively append to file name in powershell?

前端 未结 2 520
小鲜肉
小鲜肉 2021-01-16 02:38

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-         


        
2条回答
  •  广开言路
    2021-01-16 03:33

    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.

提交回复
热议问题