Powershell Move-Item Rename If File Exists - powershell

I have a powershell script that takes all of the files in one directory, renames the first one and moves it, and then goes to the next file. Sometimes there will be multiple files that get renamed to the same name (because of the system that it's going to, not ideal and will have to change) and was overwriting files that shouldn't have been getting overwritten with -force. I need all of the files to move but also have unique names so we have them available in the destination location. Is there an easy way to have it automatically rename so it would look like:
123.txt
123(1).txt
123(2).txt
or
123.txt
123_1.txt
123_2.txt

There's no built-in way to do that. Give this a try:
$src = "d:\temp"
$dest = "d:\temp1"
$num=1
Get-ChildItem -Path $src -Filter *.txt -Recurse | ForEach-Object {
$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
}

#Fixed solution basing on previous answer (variable $num moved into for each loop):
$src = "d:\temp"
$dest = "d:\temp1"
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
}

So I'm way late to the party here but... I kinda liked the responses just didn't like the lack of it being in a function so... I modified it for re-usability.
I added the Name field because I'm using this as part of another process where I am using a regex substring process to take a file name which contains <owner>-<date_string>-<hash>.<extension> and moving them into a <file_path>/<owner>/<date_string>.<extension> format.
function Move-Item-AutoRename {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline)]
[String]$Source,
[Parameter(Mandatory = $true)]
[String]$Destination,
[Parameter(Mandatory = $true)]
[String]$Name
)
PROCESS {
$count = 1
[System.IO.FileInfo]$nextName = Join-Path -Path $Destination -ChildPath $Name
while (Test-Path -Path $nextName) {
$nextName = Join-Path -Path $Destination ($Name.Split(".")[0] + "_$($count)" + $nextName.Extension)
$count += 1
}
Move-Item -Path $Source -Destination $nextName
}
}
Since I am sure others may be interested in the other part of this solution I will include it just for sharing sake.
function Import-UnsortedFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline)]
[String]$Source,
[Parameter(Mandatory = $true)]
[String]$Destination
)
PROCESS {
Get-ChildItem -Path $Source -Include *.xml, *.json | ForEach-Object {
$results = $_.Name | Select-String -Pattern '^(.*)-([0-9]*_[0-9]*)-.*(\..*)$'
[System.IO.FileInfo] $dest = (Join-Path -Path $Target -ChildPath "$($results.Matches.Groups[1])/$($results.Matches.Groups[2])$($results.Matches.Groups[3])");
$test = (Test-Path -Path $dest.Directory.FullName -PathType Container)
if ($test -eq $false) {
New-Item -Path $dest.Directory.FullName -ItemType Directory;
}
Move-Item-AutoRename -Source $_.FullName -Destination $dest.Directory.FullName -Name $dest.Name
}
}
}
Call this by invoking Import-UnsortedFiles -Source $Source -Destination $Target

Related

PowerShell Copy All Files in Folders and Sub=Folders Not older than 300 minutes

I am trying to copy all files in folders and sub-folders not older than 300 minutes, but the code I got working only copies the files in the main folder, it doesn't copy the files in subfolders.
At the destination I don't want to maintain the folder structure of the original files, I just want to put all the origin files into a single specific destination folder.
This is the code I have:
Powershell -NoL -NoP -C "&{$ts=New-TimeSpan -M 300;"^
"Get-ChildItem "C:\Origin" -Filter '*.dat'|?{"^
"$_.LastWriteTime -gt ((Get-Date)-$ts)}|"^
%%{Copy-Item $_.FullName 'C:\Destination'}}"
Could someone help me out please?
Thanks in advance.
Here's a modified script for you you can save as "Copy-Unique.ps1" you can run from a batch file.
function Copy-Unique {
# Copies files to a destination. If a file with the same name already exists in the destination,
# the function will create a unique filename by appending '(x)' after the name, but before the extension.
# The 'x' is a numeric sequence value.
[CmdletBinding(SupportsShouldProcess)] # add support for -WhatIf switch
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
[Alias("Path")]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[string]$SourceFolder,
[Parameter(Mandatory = $true, Position = 1)]
[string]$DestinationFolder,
[Parameter(Mandatory = $false)]
[int]$NewerThanMinutes = -1,
[Parameter(Mandatory = $false)]
[string]$Filter = '*',
[switch]$Recurse
)
# create the destination path if it does not exist
if (!(Test-Path -Path $DestinationFolder -PathType Container)) {
Write-Verbose "Creating folder '$DestinationFolder'"
$null = New-Item -Path $DestinationFolder -ItemType 'Directory' -Force
}
# get a list of file FullNames in this source folder
$sourceFiles = #(Get-ChildItem -Path $SourceFolder -Filter $Filter -File -Recurse:$Recurse)
# if you want only files not older than x minutes, apply an extra filter
if ($NewerThanMinutes -gt 0) {
$sourceFiles = #($sourceFiles | Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-$NewerThanMinutes) })
}
foreach ($file in $sourceFiles) {
# get an array of all filenames (names only) of the files with a similar name already present in the destination folder
$destFiles = #((Get-ChildItem $DestinationFolder -File -Filter "$($file.BaseName)*$($file.Extension)").Name)
# for PowerShell version < 3.0 use this
# $destFiles = #(Get-ChildItem $DestinationFolder -Filter "$baseName*$extension" | Where-Object { !($_.PSIsContainer) } | Select-Object -ExpandProperty Name)
# construct the new filename
$newName = $file.Name
$count = 1
while ($destFiles -contains $newName) {
$newName = "{0}({1}){2}" -f $file.BaseName, $count++, $file.Extension
}
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $DestinationFolder -ChildPath $newName
Write-Verbose "Copying '$($file.FullName)' as '$newFile'"
$file | Copy-Item -Destination $newFile -Force
}
}
# you can change the folder paths, file pattern to filter etc. here
$destFolder = Join-Path -Path 'C:\Destination' -ChildPath ('{0:yyyy-MM-dd_HH-mm}' -f (Get-Date))
Copy-Unique -SourceFolder "C:\Origin" -DestinationFolder $destFolder -Filter '*.dat' -Recurse -NewerThanMinutes 300
Changed the code to now take a datetime object to compare against rather than an amount of minutes. This perhaps makes the code easier to understand, but certainly more flexible.
function Copy-Unique {
# Copies files to a destination. If a file with the same name already exists in the destination,
# the function will create a unique filename by appending '(x)' after the name, but before the extension.
# The 'x' is a numeric sequence value.
[CmdletBinding(SupportsShouldProcess)] # add support for -WhatIf switch
Param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
[Alias("Path")]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[string]$SourceFolder,
[Parameter(Mandatory = $true, Position = 1)]
[string]$DestinationFolder,
[string]$Filter = '*',
[datetime]$NewerThan = [datetime]::MinValue,
[switch]$Recurse
)
# create the destination path if it does not exist
if (!(Test-Path -Path $DestinationFolder -PathType Container)) {
Write-Verbose "Creating folder '$DestinationFolder'"
$null = New-Item -Path $DestinationFolder -ItemType 'Directory' -Force
}
# get a list of file FullNames in this source folder
$sourceFiles = #(Get-ChildItem -Path $SourceFolder -Filter $Filter -File -Recurse:$Recurse)
# if you want only files newer than a certain date, apply an extra filter
if ($NewerThan -gt [datetime]::MinValue) {
$sourceFiles = #($sourceFiles | Where-Object { $_.LastWriteTime -gt $NewerThan })
}
foreach ($file in $sourceFiles) {
# get an array of all filenames (names only) of the files with a similar name already present in the destination folder
$destFiles = #((Get-ChildItem $DestinationFolder -File -Filter "$($file.BaseName)*$($file.Extension)").Name)
# for PowerShell version < 3.0 use this
# $destFiles = #(Get-ChildItem $DestinationFolder -Filter "$baseName*$extension" | Where-Object { !($_.PSIsContainer) } | Select-Object -ExpandProperty Name)
# construct the new filename
$newName = $file.Name
$count = 1
while ($destFiles -contains $newName) {
$newName = "{0}({1}){2}" -f $file.BaseName, $count++, $file.Extension
}
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $DestinationFolder -ChildPath $newName
Write-Verbose "Copying '$($file.FullName)' as '$newFile'"
$file | Copy-Item -Destination $newFile -Force
}
}
# you can change the folder paths, file pattern to filter etc. here
$destFolder = Join-Path -Path 'D:\Destination' -ChildPath ('{0:yyyy-MM-dd_HH-mm}' -f (Get-Date))
Copy-Unique -SourceFolder "C:\Origin" -DestinationFolder $destFolder -Filter '*.dat' -Recurse -NewerThan (Get-Date).AddMinutes(-300)
When you have saved the above code to let's say 'C:\Scripts\Copy-Unique.ps1' you can then call it from a batch file like:
Powershell.exe -NoLogo -NoProfile -File "C:\Scripts\Copy-Unique.ps1"

How to copy zip folder in powershell

How to copy the zip folder using user input values instead of hardcoded and also after placing the zip in visual studio distination folder need to clean and build the .csproj
$source = "C:\Users\name\Downloads"
$archive = "C:\Users\name\Downloads"
$Name = "xyz.zip"
$destination = "D:\repo"
$ArchiveFile = Join-Path -Path $archive -ChildPath $Name
Copy-Item -Path $ArchiveFile -Destination $destination -Force
Parameterize your variables!
Enclose you code in a function definition,
function Copy-BuildArchive
{
param()
$archive = "C:\Users\name\Downloads"
$Name = "xyz.zip"
$destination = "D:\repo"
$ArchiveFile = Join-Path -Path $archive -ChildPath $Name
Copy-Item -Path $ArchiveFile -Destination $destination -Force
}
Take the variables that you wish to substitute with user input, and move them into the param block:
function Copy-BuildArchive
{
param(
[string]$Name,
[string]$Archive,
[string]$Destination
)
$ArchiveFile = Join-Path -Path $Archive -ChildPath $Name
Copy-Item -Path $ArchiveFile -Destination $Destination -Force
}
Add some rudimentary input validation, and perhaps some default values:
function Copy-BuildArchive
{
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[string]$Archive = $(Join-Path $HOME "Downloads"),
[string]$Destination = $(Join-Path $HOME "Documents")
)
$ArchiveFile = Join-Path -Path $Archive -ChildPath $Name
Copy-Item -Path $ArchiveFile -Destination $Destination -Force
}
Congratulations - you just turned your script into a parameterized function!
Now, after executing the function definition, you can do:
Copy-BuildArchive -Name project.zip -Archive C:\build\path -Destination C:\output\path
or
Copy-BuildArchive -Name somethingElse.zip # using default values for Archive and Destination

Powershell move file with backup (like mv --backup=numbered)

I'm looking if there's a PS command that'd be equal to mv --backup=numbered, and can't find anything.
In essence, move 'file' to 'file.old', but if 'file.old' exists, 'file' should be moved to 'file.old.2'.
For now the closest I found is from this link: https://www.pdq.com/blog/copy-individual-files-and-rename-duplicates/:
$SourceFile = "C:\Temp\File.txt"
$DestinationFile = "C:\Temp\NonexistentDirectory\File.txt"
If (Test-Path $DestinationFile) {
    $i = 0
    While (Test-Path $DestinationFile) {
        $i += 1
        $DestinationFile = "C:\Temp\NonexistentDirectory\File$i.txt"
    }
} Else {
    New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -Force
It seems quite awful to have this amount of code. Is there anything simpler available?
Indeed there is no built-in function to do that. However, it should not be a problem to use a function of your own for that purpose.
How about this:
function Copy-FileNumbered {
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[ValidateScript({Test-Path -Path $_ -PathType Leaf})]
[string]$SourceFile,
[Parameter(Mandatory = $true, Position = 1)]
[string]$DestinationFile
)
# get the directory of the destination file and create if it does not exist
$directory = Split-Path -Path $DestinationFile -Parent
if (!(Test-Path -Path $directory -PathType Container)) {
New-Item -Path $directory -ItemType 'Directory' -Force
}
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($DestinationFile)
$extension = [System.IO.Path]::GetExtension($DestinationFile) # this includes the dot
$allFiles = Get-ChildItem $directory | Where-Object {$_.PSIsContainer -eq $false} | Foreach-Object {$_.Name}
$newFile = $baseName + $extension
$count = 1
while ($allFiles -contains $newFile) {
$newFile = "{0}({1}){2}" -f $baseName, $count, $extension
$count++
}
Copy-Item -Path $SourceFile -Destination (Join-Path $directory $newFile) -Force
}
This will create a new file in the destination like File(1).txt
Of course, if you rather have names like File.2.txt, just change the format template "{0}({1}){2}" to "{0}.{1}{2}"
Use the function like
$SourceFile = "C:\Temp\File.txt"
$DestinationFile = "C:\Temp\NonexistentDirectory\File.txt"
Copy-FileNumbered -SourceFile $SourceFile -DestinationFile $DestinationFile

How do I move multiple files with the same name and extension that are in subfolders into one main folder [duplicate]

I have a powershell script that takes all of the files in one directory, renames the first one and moves it, and then goes to the next file. Sometimes there will be multiple files that get renamed to the same name (because of the system that it's going to, not ideal and will have to change) and was overwriting files that shouldn't have been getting overwritten with -force. I need all of the files to move but also have unique names so we have them available in the destination location. Is there an easy way to have it automatically rename so it would look like:
123.txt
123(1).txt
123(2).txt
or
123.txt
123_1.txt
123_2.txt
There's no built-in way to do that. Give this a try:
$src = "d:\temp"
$dest = "d:\temp1"
$num=1
Get-ChildItem -Path $src -Filter *.txt -Recurse | ForEach-Object {
$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
}
#Fixed solution basing on previous answer (variable $num moved into for each loop):
$src = "d:\temp"
$dest = "d:\temp1"
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
}
So I'm way late to the party here but... I kinda liked the responses just didn't like the lack of it being in a function so... I modified it for re-usability.
I added the Name field because I'm using this as part of another process where I am using a regex substring process to take a file name which contains <owner>-<date_string>-<hash>.<extension> and moving them into a <file_path>/<owner>/<date_string>.<extension> format.
function Move-Item-AutoRename {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline)]
[String]$Source,
[Parameter(Mandatory = $true)]
[String]$Destination,
[Parameter(Mandatory = $true)]
[String]$Name
)
PROCESS {
$count = 1
[System.IO.FileInfo]$nextName = Join-Path -Path $Destination -ChildPath $Name
while (Test-Path -Path $nextName) {
$nextName = Join-Path -Path $Destination ($Name.Split(".")[0] + "_$($count)" + $nextName.Extension)
$count += 1
}
Move-Item -Path $Source -Destination $nextName
}
}
Since I am sure others may be interested in the other part of this solution I will include it just for sharing sake.
function Import-UnsortedFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline)]
[String]$Source,
[Parameter(Mandatory = $true)]
[String]$Destination
)
PROCESS {
Get-ChildItem -Path $Source -Include *.xml, *.json | ForEach-Object {
$results = $_.Name | Select-String -Pattern '^(.*)-([0-9]*_[0-9]*)-.*(\..*)$'
[System.IO.FileInfo] $dest = (Join-Path -Path $Target -ChildPath "$($results.Matches.Groups[1])/$($results.Matches.Groups[2])$($results.Matches.Groups[3])");
$test = (Test-Path -Path $dest.Directory.FullName -PathType Container)
if ($test -eq $false) {
New-Item -Path $dest.Directory.FullName -ItemType Directory;
}
Move-Item-AutoRename -Source $_.FullName -Destination $dest.Directory.FullName -Name $dest.Name
}
}
}
Call this by invoking Import-UnsortedFiles -Source $Source -Destination $Target

Using Powershell I want to Forcefully Copy Folder/files Without Erasing Extra Files in Existing Destination Folder:

I am using Powershell and am trying to forcefully copy folder/files without erasing any extra files in existing destination folders. I am stuck trying to get a working command.
Below is my code, any suggestions on how to fix this?
Copy-Item -Force -Recurse –Verbose $releaseDirectory -Destination $sitePath
you need to be sure that
$realeseDirectory
is something like
c:\releasedirectory\*
Copy-item never delete extra files or folders in destination, but with -force it will owerwrite if file already exists
Your question isn't very clear. So, you might have to tweak the function below a bit. By the way, if you are attempting to deploy a web site, copying a directory isn't the best way.
function Copy-Directory
{
param (
[parameter(Mandatory = $true)] [string] $source,
[parameter(Mandatory = $true)] [string] $destination
)
try
{
Get-ChildItem -Path $source -Recurse -Force |
Where-Object { $_.psIsContainer } |
ForEach-Object { $_.FullName -replace [regex]::Escape($source), $destination } |
ForEach-Object { $null = New-Item -ItemType Container -Path $_ }
Get-ChildItem -Path $source -Recurse -Force |
Where-Object { -not $_.psIsContainer } |
Copy-Item -Force -Destination { $_.FullName -replace [regex]::Escape($source), $destination }
}
catch
{
Write-Error "$($MyInvocation.InvocationName): $_"
}
}
$releaseDirectory = $BuildFilePath + $ProjectName + "\" + $ProjectName + "\bin\" + $compileMode + "_PublishedWebsites\" + $ProjectName
$sitePath = "\\$strSvr\c$\Shared\WebSites"
Copy-Directory $releaseDirectory $sitePath