2

I have a google drive folder that occasionally gets out of sync. Google (or someone) will append (1) to the directory/file names. Then it will remove the original directory and I'll have a bunch of folders and files named "xxx (1)".

I've been trying to write a powershell script that will crawl the directory tree and rename the folders/files by simply removing the " (1)" portion. I realize that this may result in some collisions, but I was hoping to get most of them replaced using a script. I'm not that concerned about the directory structure, I'll restore if needed, but it's just kind of a nuisance.

I've tried several powershell scripts, and the closest I've come so far is this...

This will return the correct NEW folder names

Get-ChildItem -Path "* (1)" -Recurse | select { $_.FullName.Replace(" (1)", "")}

So I tried this...

Get-ChildItem -Path "* (1)" -Recurse | Replace-Item -Path $_.FullName -Destination $_.FullName.Replace(" (1)", "") -WhatIf 

I get the error "You cannot call a method on a null-valued expression."

You cannot call a method on a null-valued expression.
At line:1 char:1
+ Get-ChildItem -Path "* (1)" -Recurse | rename-item $_.FullName  $_.Fu ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
rMa
  • 79
  • 1
  • 9

2 Answers2

3

Try surrounding the last half in a foreach-object, using Rename-Item instead of Replace-Item, etc etc:

Get-ChildItem -Path "* (1)" -Recurse | ForEach-Object {Rename-Item -Path $_.Fullname -NewName $($_.Name.Replace(" (1)", "")) -WhatIf}
Mark Wragg
  • 22,105
  • 7
  • 39
  • 68
scrthq
  • 1,039
  • 11
  • 17
  • I think that did it. Thanks. @nferrell So much magic in this Powershell that I still need to learn. – rMa Mar 23 '17 at 20:16
1

$_ is only a variable inside a scriptblock. With:

.. | select { $_.FullName.Replace(" (1)", "")}

You can get-help select-object and see the first positional parameter is -Property, so this is short for

.. | Select-Object -Property { $_.FullName.Replace(" (1)", "") }

And now you can see the scriptblock gets the files in via the pipeline, accesses them as $_ and calculates a new Property for the output, and the $_ is used once for each file. When you try to use the same technique in your second line:

.. | Replace-Item -Path $_.FullName -Destination $_.FullName.Replace(" (1)", "") -WhatIf 

The $_ is just free-floating, you can't use it like that, outside a {} scriptblock it doesn't mean anything. And you're piping the input files in through the pipeline and also trying to specify them with -Path, which is doubling up - clashing and redundant. And you're using Replace instead of Rename.

So in the other answer, nferrell uses ForEach to create a scriptblock. And pipes the files into ForEach and then specifies their name to the -Path of Rename-Item.

Which works, but it's wordy and roundabout. Why take the filenames out of the pipeline and use ForEach to shuffle them round to the other end of the cmdlet, only to put them straight back in?

Get-ChildItem .. | Rename-Item -NewName { $_.Name.Replace(" (1)", "") }

Files go in by the pipeline, NewName is calculated. No loop, no doubling up of input, no

TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87
  • 1
    Thanks @TessellatingHeckler. I wish I could give you points. I am too new apparently to grant any, but I really appreciate this thorough response. – rMa Mar 25 '17 at 23:07