How to recursively append to file name in powershell?

前端 未结 2 513
小鲜肉
小鲜肉 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:27

    The below command will return ALL files from the current folder and sub-folders within the current directory the command is executed from.

    Get-ChildItem -Recurse
    

    Because of this you are also re-turning all the files you have already updated to have the _old suffix.

    What you need to do is use the -Include -Exclude paramters of the Get-Childitem Cmdlet in order to ignore files that already have the _old suffix, and meet your include criteria, for example.

    Get-ChildItem -Recure -Include "*.txt" -Exclude "*_old"
    

    Then pipe the results into your re-name item command

    Get-ChildItem cmdlet explanation can be found here.

    https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7

    0 讨论(0)
  • 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.

    0 讨论(0)
提交回复
热议问题