Powershell Move Files To Directory - Maintain Paths? - powershell

Trying to make a simple backup script that will do as below:
Move the following
\source\example1.txt
\source\path\example2.txt
To
\dest\example1.txt
\dest\path\example2.txt
At the same time, renaming any files that already exist in dest.
My Code:
$src = "C:\Users\User\Desktop\test1"
$dest = "C:\Users\User\Desktop\test2"
Get-ChildItem -Path $src -Filter *.txt -Recurse | ForEach-Object {
$num=1
$nextName = Join-Path -Path $dest -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $dest ($_.BaseName + " ($num)" + $_.Extension)
$num+=1
}
$_ | Move-Item -Destination $nextName
}
This almost works but it flattens all files into one folder in the dest.
(\source\path\example.txt becomes \dest\example.txt)
How to fix?

Following commented code snippet could do the job:
Get-ChildItem -Path $src -Filter *.txt -Recurse |
ForEach-Object {
$num=1
# ChildPath ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
$nextName = Join-Path -Path $dest -ChildPath $_.FullName.Replace("$src\", '')
# effective destination for current file
$destNew = $nextName | Split-Path
# create effective destination silently if necessary
if ( -not (Test-Path -Path $destNew) ) {
$null = New-Item $destNew -ItemType Directory
}
while(Test-Path -Path $nextName)
{
# ↓↓↓↓↓↓↓↓
$nextName = Join-Path $destNew ($_.BaseName + " ($num)" + $_.Extension)
$num+=1
}
$_ | Move-Item -Destination $nextName
}

Other method :
function Get-ItemNameWithNumberIfExist($NewPath, $BaseName, $Extension, $rang)
{
#build a new file name
if ($rang -eq 0)
{
$NewFileName="{0}{1}" -f $NewPath, $BaseName, $Extension
}
else
{
$NewFileName="{0}({1}){2}" -f $NewPath, $BaseName, $rang, $Extension
}
#build a new path file name (use combine for work on every SE)
$NewPathFile=[System.IO.Path]::Combine($NewPath, $NewFileName)
#recursive call if file exist
if (Test-Path -Path $NewPathFile)
{
$rang++
Get-ItemNameWithNumberIfExist $NewPath $BaseName $Extension $rang
}
else
{
$NewPathFile
}
}
$OldPath='C:\temp\tmp1\'
$NewPath='C:\temp\tmp2\'
Get-ChildItem $OldPath -file -Recurse | %{
$NewPath=$_.DirectoryName.Replace($OldPath, $NewPath)
#create directory without error if exist
New-Item -ItemType Directory -Path $NewPath -Force
#move item and rename if exist
move-item $_.FullName (Get-ItemNameWithNumberIfExist $NewPath $_.BaseName $_.Extension 0)
}

Related

Powershell: Find folders with a specific name, and move contents up one level, rename if exists

I've got the first two-thirds of this one accomplished, but I'm stuck on the last part. I've got a script that searches for subfolders with a specific name, and moves their contents up one level. I have another script that moves files from one place to another, and renames them if the file already exists. What I'm trying to do now is merge the two. So here's the one that moves files up:
$sourceDir="E:\Deep Storage"
$searchFolder="Draft Materials"
Get-ChildItem -path $sourceDir -filter $searchFolder -Recurse |
ForEach-Object {
Get-ChildItem -File -Path $_.FullName |
ForEach-Object {
Move-Item -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath)
}
}
And here's the one that moves things while renaming if they already exist:
$sourceDir="E:\Test1"
$targetDir="E:\Deep Storage\Test1"
Get-ChildItem -Path $sourceDir -Filter *.* -Recurse | ForEach-Object {
$num=1
$nextName = Join-Path -Path $targetDir -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $targetDir ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Copy-Item -Destination $nextName -Verbose
}
And lastly, my attempt to hybridize the two:
$sourceDir="E:\Deep Storage"
$searchFolder="Draft Materials"
Get-ChildItem -path $sourceDir -filter $searchFolder -Recurse |
ForEach-Object {
Get-ChildItem -File -Path $_.FullName |
ForEach-Object {
$num=1
$nextName = Join-Path -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath)
while(Test-Path -Path $nextName)
{
$nextName = Join-Path -Path $_.FullName -Destination $(Split-Path -Parent $_.PSParentPath) ($_.BaseName + "_$num" + $_.Extension)
$num+=1
}
$_ | Copy-Item -Destination $nextName
}
}
I feel like I'm on the right track, but after two hours of attempts I haven't been able to get this to work.
EDIT: providing the exact syntax I'm giving it
$sourceDir="E:\Deep Storage\Projects"
$searchFolder="Draft Materials"
$destinationPath = "$($sourceDir)\.."
Write-Host "OPERATION: Search for Folders Named" -ForegroundColor White -BackgroundColor DarkGreen -NoNewLine;
Write-Host " '$searchFolder' " -ForegroundColor Yellow -BackgroundColor DarkGreen -NoNewLine;
Write-Host "and Move Contents Up One Level" -ForegroundColor White -BackgroundColor DarkGreen;
Write-Host "SEARCHDIR: $sourceDir" -ForegroundColor White -BackgroundColor DarkGreen;
# Get all directories in specific folders inside the source directory
$folders = Get-ChildItem -Path $sourceDir -Directory | Where-Object {$_.Name -like "$searchFolder*" -or $_.FullName -like "*\$searchFolder\*"}
foreach ($folder in $folders) {
# Get all files in the current folder
$files = Get-ChildItem -Path $folder.FullName
foreach ($file in $files) {
$destinationFile = Join-Path -Path $destinationPath -ChildPath $file.Name
if (Test-Path $destinationFile) {
# If a file with the same name exists in the destination directory, rename it
$name = $file.Name
$extension = $file.Extension
$i = 0
while (Test-Path $destinationFile) {
$i++
$name = "{0}_{1}{2}" -f ($file.BaseName, $i, $extension)
$destinationFile = Join-Path -Path $destinationPath -ChildPath $name
}
Write-Host "Renaming $($file.Name) to $name"
}
Move-Item $file.FullName $destinationFile -Verbose -WhatIf
}
}
Here's what I came up with reading OP's post, code, and comments:
$sourcePath = 'E:\Deep Storage\Projects'
$searchFolder = 'Draft Materials'
Write-Host "OPERATION: Search for Folders Named" -ForegroundColor White -BackgroundColor DarkGreen -NoNewLine;
Write-Host " '$searchFolder' " -ForegroundColor Yellow -BackgroundColor DarkGreen -NoNewLine;
Write-Host "and Move Contents Up One Level" -ForegroundColor White -BackgroundColor DarkGreen;
Write-Host "SEARCHDIR: $sourcePath" -ForegroundColor White -BackgroundColor DarkGreen;
# Get all directories in specific folders inside the source directory
$folders = (Get-ChildItem -LiteralPath $sourcePath -Directory -Recurse) | Where-Object Name -match $searchFolder
### Check selected folders
$Folders.FullName | Out-GridView -Title 'Selected Folders'
Read-Host 'Paused. Check selected folders in GridView. Press <enter> to continue '
###
ForEach ($folder in $folders)
{
# Get all files in the current folder
$filesToCopy = $folder | Get-ChildItem -File
# Get list of names for exising files in target (parent folder)
$targetPath = $folder.Parent.FullName
$filesInTarget = (Get-ChildItem -LiteralPath $targetPath -File).Name
ForEach ($file in $filesToCopy)
{
If ($file.Name -notIn $filesInTarget)
{
$file | Move-Item -Destination $targetPath -Verbose -WhatIf
}
Else
{
$i = 0
Do
{
$newName = '{0}_{1}{2}' -f ($file.BaseName, $i++, $file.Extension)
} Until ( $newName -notIn $FilesInTarget )
Write-Host ('Renaming "{0}" to "{1}"...' -f $file.Name , $newName)
$file | Move-Item -Destination (Join-Path $targetPath $newName) -Verbose -WhatIf
}
}
# Delete (hopefully empty) folder
If (!($folder | Get-ChildItem -Force))
{
$folder | Remove-Item -WhatIf
}
Else
{
Write-Host ('Items still exist in "{0}". Folder not deleted.' -f $folder.FullName)
}
}
Syntax choice: For any cmdlet that has Path/LiteralPath parameter sets (gci, Copy, Move, Rename, etc.), the System.IO.FileSystemInfo | <Cmdlet> syntax succeeds with items that would fail in the <Cmdlet> -Path (System.IO.FileSystemInfo).FullNaame form becasue special characters in their name would require the -LiteralPath parameter.
In many cases replacing -Path with -LiteralPath (or its alias: -lp) will work as well. But the pipelined format reads "cleaner" (IMHO) when scanning code and, if you're just learning PowerShell, reminds you to think in terms of pipelining whenever possible and avoiding intermediate variables. Just for grins, here's a version of the above code where items are piped as much as possible, using ForEach-Object:
(Get-ChildItem -LiteralPath $sourcePath -Directory -Recurse) |
where Name -match $searchFolder |
ForEach-Object {
# Get list of names for exising files in target (parent folder)
$targetPath = $_.Parent.FullName
$filesInTarget = (Get-ChildItem -LiteralPath $targetPath -File).Name
$_ | Get-ChildItem -File | ForEach-Object {
If ($_.Name -notIn $filesInTarget)
{
$_ | Move-Item -Destination $targetPath -Verbose -WhatIf
}
Else
{
$i = 0
Do
{
$newName = '{0}_{1}{2}' -f ($_.BaseName, $i++, $_.Extension)
} Until ( $newName -notIn $FilesInTarget )
Write-Host ('Renaming "{0}" to "{1}"...' -f $_.Name , $newName)
$_ | Move-Item -Destination (Join-Path $targetPath $newName) -Verbose -WhatIf
}
}
# Delete (hopefully empty) folder
If (!($_ | Get-ChildItem -Force))
{
$_ | Remove-Item -WhatIf
}
Else
{
Write-Host ('Items still exist in "{0}". Folder not deleted.' -f $_.FullName)
}
}
please try this:
$sourcePath = "C:\temp\test\Test"
$destinationPath = "$($sourcePath)\.."
# Get all files in the source directory
$files = Get-ChildItem -Path $sourcePath
foreach ($file in $files) {
$destinationFile = Join-Path -Path $destinationPath -ChildPath $file.Name
if (Test-Path $destinationFile) {
# If a file with the same name exists in the destination directory, rename it
$name = $file.Name
$extension = $file.Extension
$i = 0
while (Test-Path $destinationFile) {
$i++
$name = "{0}_{1}{2}" -f ($file.BaseName, $i, $extension)
$destinationFile = Join-Path -Path $destinationPath -ChildPath $name
}
Write-Host "Renaming $($file.Name) to $name"
}
Move-Item $file.FullName $destinationFile
}
This will not delete the original location and will move everything from the sourcePath to the parent location.
To delete the original location just add at the end:
Remove-Item -Path $sourcePath -Force -Confirm:$false
UPDATE1:
$sourcePath = "C:\temp\test\Test"
$destinationPath = "$($sourcePath)\.."
# Get all directories in specific folders inside the source directory
$folders = Get-ChildItem -Path $sourcePath -Directory | Where-Object {$_.Name -like "Folder1*" -or $_.FullName -like "*\Folder2\*"}
foreach ($folder in $folders) {
# Get all files in the current folder
$files = Get-ChildItem -Path $folder.FullName
foreach ($file in $files) {
$destinationFile = "$($folder.FullName)\.."
if (Test-Path $destinationFile) {
# If a file with the same name exists in the destination directory, rename it
$name = $file.Name
$extension = $file.Extension
$i = 0
while (Test-Path $destinationFile) {
$i++
$name = "{0}_{1}{2}" -f ($file.BaseName, $i, $extension)
$destinationFile = Join-Path -Path $destinationPath -ChildPath $name
}
Write-Host "Renaming $($file.Name) to $name"
}
Move-Item $file.FullName $destinationFile
}
}

Powershell copy only selected files with folder structure

I have a folder hierarchy with a lot of files.
I need to copy all folders and only selected files. For this purposes I write script:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
Copy-Item $files -Destination $Destination -recurse
When I execute variable $files, it returns correct path:
But when I execute Copy-Item it returns not full path:
Perhaps my approach is wrong. If so, how to copy entire folder structure, and only selected files (in this case system.serviceModel.client.config file)?
UPD1 Ok, I've found, how to copy only folders:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
Copy-Item $path $Destination -Filter {PSIsContainer} -Recurse -Force
But how to copy only selected files, preserving their location? What needs to be in $Destination variable?
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { Copy-Item -Path $_.FullName -Destination $Destination }
This code would keep the directory structure the same too
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
$fileName = "system.serviceModel.client.config"
Get-ChildItem -Path $path -Recurse | ForEach-Object {
if($_.Name -like $fileName) {
$dest = "$Destination$(($_.FullName).Replace($path,''))"
$null = New-Item $dest -Force
Copy-Item -Path $_.FullName -Destination $dest -Force
}
}
To copy the whole folder structure AND files with a certain name, below code should do what you want:
$Source = 'D:\Drop\SOA-ConfigurationManagement - Test\181'
$Destination = 'D:\test'
$FileToCopy = 'system.serviceModel.client.config'
# loop through the source folder recursively and return both files and folders
Get-ChildItem -Path $Source -Recurse | ForEach-Object {
if ($_.PSIsContainer) {
# if it's a folder, create the new path from the FullName property
$targetFolder = Join-Path -Path $Destination -ChildPath $_.FullName.Substring($Source.Length)
$copyFile = $false
}
else {
# if it's a file, create the new path from the DirectoryName property
$targetFolder = Join-Path -Path $Destination -ChildPath $_.DirectoryName.Substring($Source.Length)
# should we copy this file? ($true or $false)
$copyFile = ($_.Name -like "*$FileToCopy*")
}
# create the target folder if this does not exist
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
if ($copyFile) {
$_ | Copy-Item -Destination $targetFolder -Force
}
}
try this
$path = 'D:\Drop\SOA-ConfigurationManagement - Test\181\'
$Destination = 'D:\test\'
$files = Get-ChildItem -Path $path -Recurse -File | where Name -like "*system.serviceModel.client.config*" | %{
$Dir=$_.DirectoryName.Replace($path, $Destination)
$NewPAthFile=$_.FullName.Replace($path, $Destination)
#create dir if not exists
New-Item -Path $Dir -ItemType Directory -Force -ErrorAction SilentlyContinue
#copy file in new dir
Copy-Item $_.FullName $NewPAthFile
}
With minimal changes I'd suggest the following:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
$files | % { $_ | Copy-Item -Destination $Destination -recurse }
You can even put the whole copy on one line:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$Destination = "D:\test\"
Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { $_ | Copy-Item -Destination $Destination -recurse }
Copy-Item can find the path from the stream of input objects but it doesn't seem to be able to take a collection of System.IO.FileInfo objects as an argument to Path.

PowerShell: Iterate Through Directory List, Copy Files with Duplicate Handling

EDIT: So after some research, since what I had wasn't quite doing the trick and the advice and comments from before got me further along than I was previously, I am back with a bit more research and a more complete script.
Import-CSV C:\saveme\file_path3.csv | ForEach-Object {
Get-ChildItem $_.path -Recurse | ForEach-Object {
$split = $_.FullName -split '\\'
$DestFile = $split[1..($split.Length - 1)] -join '\'
$DestFile = "G:\Recuva2\$DestFile"
$null = New-Item -Path $DestFile -Type File -Force
If (Test-Path $DestFile) {
$i = 0
While (Test-Path $DestFile) {
$i += 1
$DestFile = $DestFile+$i
}
} Else {
$null
}
Copy-Item -Path $_.FullName -Destination $DestFile -Verbose -Force
}
}
This seems to be breaking due to an inability to find the destination directory. It seems to be breaking on this line
$null = New-Item -Path $DestFile -Type File -Force
and this line
Copy-Item -Path $_.FullName -Destination $DestFile -Verbose -Force
The common denominator here seems to be the $DestFile. I understand I am doing a good bit to the $DestFile, but I can't seem to nail down what is causing it to break.
My desired end-result here is that the folder structure be maintained when copying the specific files over in the csv list from which I am importing.
What actually seems to be happening is that it is throwing errors whenever I attempt to copy. Error text below.
Copy-Item : Could not find a part of the path 'G:\Recuva2\HR_VIOLATORS\REPORTS\REPORT_Storage2\199452\1.3.6.1.4.1.11157.2011.3.21.8.12.5.52516\1.3.51.5156.1369.20110321.1190709\1.3.51.0.7.3750462839.61413.18976.39828.11247.2380.39394'.
At line:16 char:5
+ Copy-Item -Path $_.FullName -Destination $DestFile -Verbose -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Copy-Item], DirectoryNotFoundException
+ FullyQualifiedErrorId : System.IO.DirectoryNotFoundException,Microsoft.PowerShell.Commands.CopyItem
Command
-- END EDIT
ORIGNAL POST:
Similar to but not quite the same as
Keep rename duplicate items with copy
Essentially I am being forced to grab a list of directories from a csv file and copy their contents. However the problem I am having is that some of the directories and files are duplicated.
I am attempting to use this script to import from the csv, copy the files with directory structure intact, while renaming any duplicates, ideally at the file level so that the duplicate directories are merged.
Import-CSV C:\COPYME\file_path.csv | foreach($_.path) {
ls $_.path -recurse | foreach($_) {
$SourceFile = $_.FullName
$DestinationFile = "G:\Recuva2\"+$_
If (Test-Path $DestinationFile) {
$i = 0
While (Test-Path $DestinationFile) {
$i += 1
$DestinationFile = "G:\Recuva2\"+$_+$i
}
} Else {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -verbose -Force
}
}
I am receiving an error that says that line 2 character 24 cannot be resolved to a method? What am I missing/doing wrong here?
There is a difference between the ForEach-Object cmdlet and the ForEach loop.
Import-Csv -Path 'C:\COPYME\file_path.csv' |
ForEach-Object {
ls $_.path -recurse |
ForEach-Object {
$SourceFile = $_.FullName
$DestinationFile = "G:\Recuva2\"+$_
If (Test-Path $DestinationFile) {
$i = 0
While (Test-Path $DestinationFile) {
$i += 1
$DestinationFile = "G:\Recuva2\"+$_+$i
}
} Else {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -verbose -Force
}
}
With PowerShell 4.0+ there is a .foreach() method. But it looks like you are really trying to use the alias to ForEach-Object which as iRon has pointed out in the comments does not need the ($_).
Import-CSV C:\COPYME\file_path.csv | ForEach-Object {
Get-ChildItem $_.Path -Recurse | ForEach-Object {
$SourceFile = $_.FullName
$DestinationFile = "G:\Recuva2\" + $_
If (Test-Path $DestinationFile) {
$i = 0
While (Test-Path $DestinationFile) {
$i += 1
$DestinationFile = "G:\Recuva2\" + $_ + $i
}
} Else {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -Verbose -Force
}
}
As it turned out, I was causing my own problems by trying to pass data I had already gotten. A working version of the scripting is below. This worked flawlessly for every test I put it through thus far large and small. Thank you to the answers above for contributing to my knowledge and understanding of powershell.
Import-CSV C:\COPYME\file_path.csv | ForEach-Object {
Get-ChildItem $_.path -recurse | ForEach {
$split = $_.FullName -split '\\'
$DestinationFile = $split[1..($split.Length - 1)] -join '\'
$DestinationFile = "D:\HUBICOPY\$DestinationFile"
#$DestinationFile = (Resolve-Path $DestinationFile).Path
#$null = New-Item -Path $DestinationFile -Type File -Force
If (Test-Path $DestinationFile)
{
$i = 0
While (Test-Path $DestinationFile)
{
$i += 1
$DestinationFile = $DestinationFile+$i
}
}
Else {New-Item -ItemType File -Path $DestinationFile -Force}
Copy-Item -Path $_ -Destination $DestinationFile -verbose -Force
}
}

File renaming with powershell

Script is working almost how it is intended, still struggling with renaming duplicate files. I cannot figure out how to get it to name the files like
filename(1).ext
filename(2).ext
the closest I have gotten was
filename(1).ext
filename(1)(2).ext
#region actual script
$srcRoot = "C:\srcLocation"
$dstRoot = "C:\dstLocation"
$fileList = Get-ChildItem -Path $srcRoot -File -Force -Recurse
foreach ($file in $fileList) {
$fileName = $file.Name.ToUpper()
$fileExt = $file.Extension.ToUpper()
$dstFileName = $null
switch -Regex ($fileName)
{
'[A-Z]{4}-[0-9]{3}' { $dstFileName = $fileName }
'[A-Z]{4} [0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})\s([0-
9]{3})','$1-$2' }
'[A-Z]{4}[0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})([0-9]
{3})','$1-$2'}
Default { Write-Warning -Message "$fileName is not an expected filename"
}
}
if ($dstFileName) {
$dstDir = $dstFileName.Split('.')[0].Substring(0,8)
$dstPath = Join-Path -Path $dstRoot -ChildPath $dstDir
if (-not (Test-Path -Path $dstPath)) {
New-Item -Path $dstPath -ItemType Directory
}
$i = 1
if (test-path $dstPath\$dstFileName){
$dstFileName = $dstFileName.Split('.')[0] + "($i)" + $fileExt
While (test-path $dstPath\$dstFileName){
$i +=1
$dstFileName = $dstFileName -replace
}
}
Write-Verbose "Moving $($file.FullName)"
Move-Item -Path $($file.FullName) -Destination $dstPath\$dstFileName -
ErrorAction Continue
}
}
#endregion
You can simply use the Replace method of string objects in PowerShell. To verify your input, you can use a RegEx. Move-Item will throw an error, if the file already exists in the destination anyways. The complete script would look like this.
#region setup
New-Item -Path C:\srcpath,C:\dstpath -ItemType Directory
Set-Location C:\srcpath
New-Item 'ABCD123.txt','ABCD 123.txt','AbCD-123.txt','AAAA111.txt','BBBB 222.jpg','BBBB-222.txt' -ItemType File
#endregion
#region actual script
$srcRoot = "C:\srcpath"
$dstRoot = "C:\dstpath"
$fileList = Get-ChildItem -Path $srcRoot -File -Force -Recurse
foreach ($file in $fileList) {
$fileName = $file.Name.ToUpper()
$dstFileName = $null
switch -Regex ($fileName)
{
'[A-Z]{4}-[0-9]{3}' { $dstFileName = $fileName }
'[A-Z]{4} [0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})\s([0-9]{3})','$1-$2' }
'[A-Z]{4}[0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})([0-9]{3})','$1-$2'}
Default { Write-Warning -Message "$fileName is not an expected filename" }
}
if ($dstFileName) {
$dstDir = $dstFileName.Split('.')[0]
$dstPath = Join-Path -Path $dstRoot -ChildPath $dstDir
if (-not (Test-Path -Path $dstPath)) {
New-Item -Path $dstPath -ItemType Directory
}
Write-Verbose "Moving $($file.FullName)"
Move-Item -Path $($file.FullName) -Destination $dstPath\$dstFileName -ErrorAction Continue
}
}
#endregion
#region result
Write-Host '----- Result -----' -BackgroundColor DarkYellow
Get-ChildItem C:\dstpath -Recurse | Select-Object -ExpandProperty FullName
#endregion
Gets the Files in Source folder (Get-ChildItems)
Renames The File to include a - instead of a " " (Rename-Item)
Sets the Child Name property to the new name ($File.Name)
Creates new Folder in source based on first 4 Chars
Moves-Item to new created folder (move-item)
$Source = "C:\Start"
$Destination = "C:\End"
foreach($File in (Get-ChildItem -Path $Source -File -Recurse)){
Rename-Item $File.Fullname ($File.Name -replace " ", "-")
$file.Name = ($File.Name -replace " ", "-")
New-Item "$($Destination)\$($File.Name.Substring(0,3))" -ItemType directory
move-item $File.FullName -force -destination $Destination\$($File.Name.Substring(0,3))
}

Powershell Copy-Item Rename If File Exists

I am using this code I found on this site in a script to copy PST files and rename the duplicate. My question and the problem I am having with it is that when it renames the .pst it continues to increment the number.
For example, if it finds a file named "test.pst" it will copy it as is. If it finds another file also named "test.pst", it will copy it and rename it "test-1.pst" which is fine. However, if it finds two files named "test2.pst" it will copy the first one as "test2.pst" and copy and rename the second one to "test2-2.pst" instead of "test2-1.pst".
Do you have any suggestions on how I can modify my code so that it will start numbering each new duplicate file with 1 (test3-1.pst, test4-1.pst, etc)?
$csv = import-csv .\test.csv
foreach ($line in $csv) {
New-Item c:\new-pst\$($line.username) -type directory
$dest = "c:\new-pst\$($line.username)"
$i=1
Get-ChildItem -Path $line.path -Filter *.pst -Recurse | ForEach-Object {
$nextName = Join-Path -Path $dest -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $dest ($_.BaseName + "_$i" + $_.Extension)
$i++
}
$_ | copy-Item -Destination $nextName -verbose
}
}
You'll need to reset the counter:
$csv = import-csv .\test.csv
foreach ($line in $csv) {
New-Item c:\new-pst\$($line.username) -type directory
$dest = "c:\new-pst\$($line.username)"
Get-ChildItem -Path $line.path -Filter *.pst -Recurse | ForEach-Object {
$i=1 # Note the position of the initializer
$nextName = Join-Path -Path $dest -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $dest ($_.BaseName + "_$i" + $_.Extension)
$i++
}
$_ | copy-Item -Destination $nextName -verbose
}
}
Moving my comment to an answer. You need to move the $i = 1 line to inside your ForEach loop as such:
$csv = import-csv .\test.csv
foreach ($line in $csv) {
New-Item c:\new-pst\$($line.username) -type directory
$dest = "c:\new-pst\$($line.username)"
Get-ChildItem -Path $line.path -Filter *.pst -Recurse | ForEach-Object {
$i=1
$nextName = Join-Path -Path $dest -ChildPath $_.name
while(Test-Path -Path $nextName)
{
$nextName = Join-Path $dest ($_.BaseName + "_$i" + $_.Extension)
$i++
}
$_ | copy-Item -Destination $nextName -verbose
}
}