Search code examples
powershellpowershell-cmdletget-childitemrename-item-cmdlet

Rename-Item : Source and destination path must be different error in Powershell


I am using Powershell and trying to return the child item of a directory, which happens to be a subdirectory, and then use the Rename-Item cmdlet to rename the subdirectory name to something else.

I feel like the following code should work:

Get-ChildItem "C:\Users\Admin\Desktop\mydirectory\subdirectory" | Rename-Item -NewName {$_.Name -replace 'test'} 

But I am getting this error:

Rename-Item : Source and destination path must be different.

What am I missing here? Thanks in advance!


Solution

  • Since you're using Get-ChildItem without limiting the result to files (via the -File switch), both files and directories can be among the output items.

    While Rename-Item results in a quiet no-op if a file is being renamed to the same name that it currently has, trying the same on a directory results in the error you saw.

    This applies to all items whose name does not contain substring 'test', in which case the
    -replace operation passes the input string through as-is.

    If your intent is to rename files only, the solution is to simply add the -File switch:

    Get-ChildItem -File "C:\Users\Admin\Desktop\mydirectory\subdirectory" |
      Rename-Item -NewName { $_.Name -replace 'test' } 
    

    If directories are (also) targeted, as in your case, you need to explicitly filter out input items for which no actual renaming would occur:

    Get-ChildItem -Directory -Filter *test* "C:\Users\Admin\Desktop\mydirectory\subdirectory" |
      Rename-Item -NewName { $_.Name -replace 'test' } 
    

    -Filter *test* ensures that only subdirectories that contain the word 'test' are output, which guarantees that actual renaming occur (though note that the command would fail if a subdirectory's entire name were 'test', as that would make the script block return the empty string).


    If you simply want to rename a single subdirectory to a fixed new name, you don't need a delay-bind script block at all:

    # NOTE: Works only if only a SINGLE subdirectory is returned.
    Get-ChildItem -Directory "C:\Users\Admin\Desktop\mydirectory\subdirectory" |
      Rename-Item -NewName 'test'
    

    If you have multiple subdirectories and you want incorporate a sequence number into the new names, you do again need a delay-bind script block:

    $num = 0
    Get-ChildItem -Directory "C:\Users\Admin\Desktop\mydirectory\subdirectory" |
      Rename-Item -NewName { 'test' + ++(Get-Variable -Scope 1 num).Value } -WhatIf
    

    This renames the subdirectories to test1, test2, ...
    For an explanation of this technique (the need for a Get-Variable call), see this answer.


    If you want to preview the renaming operations that would take place, you can add the -WhatIf common parameter to the Rename-Item call, which will show for each input file what it would be renamed to.

    However, you have to infer from the output the cases when no actual renaming takes place, due to the delay-bind script block passed to -NewName returning the same name as before.

    E.g., an input file named foo would not be renamed, because 'foo' -replace 'test' returns 'foo' unmodified, which with -WhatIf would show as follows (line breaks added for readability) - note how the target and the destination path are the same:

    What if: Performing the operation "Rename File" on target "
    Item: C:\path\to\foo 
    Destination: C:\path\to\foo
    "