Recursively renaming files in Powershell - powershell

Calling this powershell command and getting an error. Driving me nuts.
Prompt> get-childitem -recurse ./ *NYCSCA* | where-object { $_.Name -like
"*NYCSCA*" } | rename-item $_ -newname $_.Name.Replace(" ","_") -whatif
Here is the response:
You cannot call a method on a null-valued expression.
At line:1 char:140
+ get-childitem -recurse ./ *NYCSCA* | where-object { $_.Name -like "*NYCSCA*" } | select FullName | rename-item $_ -n
ewname $_.Name.Replace <<<< (" ","_") -whatif
+ CategoryInfo : InvalidOperation: (Replace:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
If I remove the last part, I get a list of files. Any clues? I have not grocked powershell yet, obviously.
Note: I tried to post this to superuser, but the site is consistently failing now - won't let me add this exact question.
Here it is greatly simplified. I cannot even get this classic example to work.
gci *NYCSCA* | ren $_ ($_.Name).Replace("foo","bar")
Thank you #JNK, the % did it. The solution I needed is this, in case you're interested:
gci -recurse | where-object{ $_.Name -like "*NYCSCA*"} | %{rename-item $_.FullName $_.FullName.Replace("NYCSCA","SDUSD") }

I think you need foreach-object:
get-childitem -recurse ./ *NYCSCA* | where-object { $_.Name -like
"*NYCSCA*" } | % {rename-item $_ -newname $_.Name.Replace(" ","_") -whatif}
The piped array can't be renamed as a set.

Here's a simplified version to rename files only
Get-ChildItem -Filter *NYCSCA* -Recurse |
Where-Object {!$_.PSIsContainer} |
Rename-Item -NewName { $_.Name.Replace(' ','_') } -WhatIf
(Edit: line breaks added for clarity)

Related

PowerShell error in renaming of subfolders

I am a PowerShell newbie. I have the following command to rename all subfolders:
Get-ChildItem -r | foreach{Rename-Item $_.FullName ($_.Name -replace "2021", "2021 renamed")}
This command works correctly, but also returns an error message:
Rename-Item : Source and destination path must be different.
At line:1 char:28
+ ... e | foreach{Rename-Item $_.FullName ($_.Name -replace "2021", "2021 r ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\Users\abc\...def\ghi\2025:String) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
Where ...def\ghi\2025 is the last subfolder.
Could someone please advise me what the error in my command is?
If you want to only rename subfolders, then you should add -Directory parameter. Also, you can pipe directly to Rename-Item, no foreach loop is required. The error message is shown for those items that you don't actually change (don't have 2021 in the name for example so the new name is the same as the old.) You could just set erroraction to silently continue.
Get-ChildItem -Directory -Recurse |
Rename-Item -NewName {$_.Name -replace "2021", "2021 renamed"} -ErrorAction SilentlyContinue
Or if you'd prefer you can stick with the loop and only attempt to rename those that actually have the matching criteria
Get-ChildItem -r | ForEach-Object{
if($_.name -match '2021')
{
Rename-Item $_.FullName ($_.Name -replace "2021", "2021 renamed")
}
}

Powershell: Get-ChildItem - piping to Rename-Item and Copy-Item

I am trying to write a script that gathers a list of server names, then iterates through each server name and grabs the second to the newest folder in a directory, renames it, and copies it to another server. I am running into trouble with getting this to work all in one command, and I am not sure what is the best way to split it up and have it work properly. I get errors when trying different things, the main one is "rename-item: cannot evaluate parameter "NewName" because its argument is specified as a script block and there is no input". Here is the code snippet that causes this error:
$serverNames = Get-Content -Path "C:\servers.txt"
foreach ($serverName in $ServerNames) {
$reportServer = "a56741035"
Get-ChildItem "\\$($serverName)\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | Rename-Item -NewName { "$serverName" + "_" + $_.Name } | Copy-Item $_.FullName -Destination "\\$($reportServer)\c$\temp\mpos\logs" }
Another is "cannot bind argument to parameter 'Path' because it is null". Here is the code that causes this error:
$serverNames = Get-Content -Path "C:\servers.txt"
foreach ($serverName in $ServerNames) {
$reportServer = "a56741035"
Get-ChildItem "\\$($serverName)\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -NewName { "$serverName" + "_" + $_.Name } | Copy-Item $_.FullName -Destination "\\$($reportServer)\c$\temp\mpos\logs" } }
I feel like I am very close, and just don't see something. Any assistance is greatly appreciated, thank you so much. Have a great day.
EDITED TO INCLUDE NEWEST CODE:
Get-ChildItem "\\$($serverName)\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName ( "$serverName" + "_" + $_.Name ) ; Copy-Item $_.FullName -Destination "\\$($reportServer)\c$\temp\mpos\logs" }
CODE FOR NEWEST COMMENTS:
Get-Child-Item "\\$serverName\d$\mposlogs\device" | where {$_.PSIsContainer} | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName ( "$serverName" + "" + $.Name ) ; Copy-Item ( "$serverName" + "" + $.Name ) -Destination "\\$reportServer\c$\temp\mpos\logs" }
So this turned out to be more difficult than I thought it would be, the trick being that Rename-Item seems to basically consume the pipelined object without giving you a chance to use that object in the subsequent name(using a scriptblock is also not really ideal, much easier to use a simple concatted string) so you need to change the Rename-Item portion of your loop slightly to
Rename-Item -Path $_.FullName -NewName ("$serverName" + "_" + $_.Name)
As far as the syntax changes prageeth suggests you can make them but they are unneeded and imo increase ambiguity(Get-Childitem does not require an escape character to access an admin share and there is nothing wrong with using $($) syntax even when not accessing a property).
EDIT
Ok after working in the comments for a bit here is an updated version of the full GCI pipeline. I tested it successfully in my environment so fingers crossed it works for you. Please let me know if any part of it doesn't make sense.
Get-ChildItem "\\$serverName\d$\mposlogs\device" | where {$_.PSIsContainer} | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName ( "$serverName" + "_" + $_.Name ) -PassThru | Copy-Item -Destination "\\$reportServer\c$\temp\mpos\logs" }
In situation like this is always best to analyse the command output first...
# Rename doesn't output anything by default
Rename-Item ToBeRenamed.txt Renamed.txt
# For most commands with that default behaviour, you can use -PassThru to force output
Rename-Item ToBeRenamed.txt Renamed.txt -PassThru
# Once we have something in the pipeline, we can pipe result to Copy-Item
Rename-Item ToBeRenamed.txt Renamed.txt -PassThru | Copy-Item -Destination C:\temp
ls C:\temp\Renamed.txt
I would recommend avoiding ForEach-Object here - it's not really needed. You just need to control output from commands, that's it.
Edit:
As user mike mentioned, it appears that admin shares doesn't need $ to be escaped with. The real problem is rename-Item cmdlet missing the -Path parameter. Thanks for bringing that up Mike!
However I still recommend removing the extra $ over $($serverName) to reduce ambiguity.
Try this!
Get-ChildItem "\\$serverName\d$\mposlogs\device" | where { $_.PSIsContainer } | Sort CreationTime -Descending | Select -Skip 1 | Select -First 1 | ForEach-Object { Rename-Item -Path $_.FullName -NewName { "$serverName" + "_" + $_.Name } | Copy-Item $_.FullName -Destination "\\$reportServer\c$\temp\mpos\logs" } }

Parameter Binding

I'm trying to create a PowerShell script that will move XML files of a certain age to a network drive to be archived. The script thus far:
$qaprocessedpath = "Y:\SFTPSHARE\SFTPYMSQ\YS42C1Processed"
$qabackup = “\\servername\S$\xmlbackup\qa"
$max_age_qa = "-1"
$curr_date = Get-Date
$del_date_q = $curr_date.AddDays($max_age_qa)
Get-ChildItem -include *.xml $qaprocessedpath | Where-Object {$_.LastWriteTime -lt $del_date_q } | Foreach-Object {Copy-Item -Path $_.FullName -Destination $qabackup} {Remove-Item $_.FullName}
This code leads to the following error:
Copy-Item : Cannot bind argument to parameter 'Path' because it is null.
At Y:\SFTPSHARE\SFTPYMSP\XMLBackup.ps1:52 char:132
+ Get-ChildItem -include *.xml $qaprocessedpath | Where-Object { $_.LastWriteTime -lt $del_date_q } | Foreach-Object {Copy-Item -Path <<<< $_.FullName -Destination $qabackup} {Remove-Item $_.FullName}
+ CategoryInfo : InvalidData: (:) [Copy-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CopyItemCommand
Not sure where the issue is. I'm a novice scripter, so I'm sure it's something obvious...
In this line
Get-ChildItem -include *.xml $qaprocessedpath | Where-Object {$_.LastWriteTime -lt $del_date_q } | Foreach-Object {Copy-Item -Path $_.FullName -Destination $qabackup} {Remove-Item $_.FullName}
Why do you have two scriptblocks following the Foreach-Object?
Try separating the copy-item and remove-item with semicolons (in the same scriptblock) if you want them both to run. As you wrote it, the two scriptblocks are getting bound to the -Process parameter (as usual) and the -Begin parameter.

Powershell Remove-Item item is Null when using get-childitem

I am having trouble with the following script, which liberally makes use of pipelines:
$date = Get-Date -date "12/31/2010 00:00 AM" -format MM-dd-yyyy
$TDrive = "C:\Users\xxxx\Desktop"
(get-childitem -path $Tdrive -recurse -force -ErrorAction SilentlyContinue) |
?{ $_.fullname -notmatch "\\Alina_NEW\\?" } |
?{ $_.fullname -notmatch "\\!macros\\?" } |
?{ $_.fullname -notmatch "\\DO_NOT_DELETE\\?" } |
Where-Object {$_.LastAccessTime -lt $date} |
Remove-Item $_
I am getting the following error when I run this, meaning that the $_ is null:
Remove-Item : Cannot bind argument to parameter 'Path' because it is null.
At line:13 char:13
+ Remove-Item $_
+ ~~
+ CategoryInfo : InvalidData: (:) [Remove-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.R
emoveItemCommand
What I am not understanding is, why would the path ever be null? This is all one fluid pipeline, and the value of $_ should persist through the entire pipeline, should it not?
My only thought is that when the where-object finds a file that is not less than that date, it returns null through the rest of the pipeline. I believe this error happens pre-execution, however, so there is a more fundamental problem here.
The point of this script is to delete all files not in the directories listed, that are older than 12/31/2010.
Two things. First, you'll want to Remove-Item Foreach of the files gci finds. Second, Remove-Item takes a path. Try something like this:
$date = Get-Date -date "12/31/2010 00:00 AM" -format MM-dd-yyyy
$TDrive = "C:\Users\xxxx\Desktop"
(get-childitem -path $Tdrive -recurse -force -ErrorAction SilentlyContinue) |
?{ $_.fullname -notmatch "\\Alina_NEW\\?" } |
?{ $_.fullname -notmatch "\\!macros\\?" } |
?{ $_.fullname -notmatch "\\DO_NOT_DELETE\\?" } |
Where-Object {$_.LastAccessTime -lt $date} |
% {Remove-Item $_.FullName -WhatIf}

replace names of all directiories and files in PS

I want to replace all space characters into "_" in names of all subfolders and files.
Unfortunately when I type:
Get-ChildItem -recurse -name | ForEach-Object { Rename-Item $_ $_.replace(" ","_") }
Error message:
Rename-Item : Source and destination path must be different. At line:1
char:60
+ Get-ChildItem -recurse -name | ForEach-Object { Rename-Item <<<< $_ $.replace(" ","") }
+ CategoryInfo : WriteError: (PATH_HERE) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
How I should improve this short code?
Don't use the Name switch, it outputs only the names of the objects, not their full path. Try this:
Get-ChildItem -Recurse | `
Where-Object {$_.Name -match ' '} | `
Rename-Item -NewName { $_.Name -replace ' ','_' }
The issue here is that if there is no space in the file name the name does not change. This is not supported by Rename-Item. You should use Move-Item instead:
Get-ChildItem -recurse -name | ForEach-Object { Move-Item $_ $_.replace(" ", "_") }
Additionally, in your answer you missed the underscore in $_.replace(...) plus you where replacing spaces with an empty string. Included this in my answer.
Adding a filter worked for me:
Get-ChildItem C:\path-to-directory -Recurse -Filter *foo* | Rename-Item -NewName { $_.name -replace 'foo', 'bar'} -verbose