Compress-Archive handle files with same name in Powershell - powershell

I'm writing an archiving script which collecting desired files to an array then adding them to an archive 1 by 1.
I came to a problem when there is DIR1/file.ext and DIR2/file.ext because DIR2's file going to overwrite the previous.
How can I set unique filename or how it's possible to solve it on the fly instead of copying files to a dir with structures then zip the whole dir?
Here is my code:
# GET FILE LIST
$outgoingfiles = Get-ChildItem -Depth 1 -Filter "*.EXT" | Where-Object { $_.DirectoryName -like "*OUTGOING*" }
# Handle if OUTGOING/archive dir is exists
if(-not (Test-Path "OUTGOING/archive")) {
New-Item -Path "OUTGOING/archive" -ItemType Directory
}
# ZIP outgoing files
ForEach ($outgoing in $outgoingfiles) {
Compress-Archive $outgoing.FullName -Update -DestinationPath $zippath
}
Thank you!

I don't think there is a way to tell Compress-Archive to rename files when a file with the same name is already included in the zip.
What you can do is create a temporary folder, copy all files to there and if needed rename them. Then create the zip file using the unique files in that folder.
Finally, remove the temp folder again:
$zippath = 'D:\Test\OutGoing.zip' # path and filename for the output zip file
$rootPath = 'D:\Test' # where the files can be found
# create a temporary folder to uniquely copy the files to
$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([Guid]::NewGuid().Guid)
$null = New-Item -ItemType Directory -Path $tempFolder
# create a hashtable to store the fileHash already copied
$fileHash = #{}
# get the list of files and copy them to a temporary folder
Get-ChildItem -Path $rootPath -Depth 1 -Filter '*.EXT' -File | Where-Object { $_.DirectoryName -like "*OUTGOING*" } | ForEach-Object {
$count = 1
$newName = $_.Name
# test if the file name is already in the hash and if so, append a counter to the basename
while ($fileHash.ContainsKey($newName)) {
$newName = "{0}({1}){2}" -f $_.BaseName, $count++, $_.Extension
}
# store this file name in the hash and copy the file
$fileHash[$newName] = $true
$newFile = Join-Path -Path $tempFolder -ChildPath $newName
$_ | Copy-Item -Destination $newFile -Force
}
# append '*.*' to the temporary folder name.
$path = Join-Path -Path $tempFolder -ChildPath '*.*'
# next, get the list of files in this temp folder and start archiving
Compress-Archive -Path $path -DestinationPath $zippath -Update
# when done, remove the tempfolder and files
Remove-Item -Path $tempFolder -Force -Recurse
Hope that helps

I would just copy the files along with their parent directories to a destination folder, then zip it up with Compress-Archive. Then you don't have to worry about making filenames unique.
Demo:
$sourceFolder = "C:\\"
$destinationFolder = "C:\\OUTGOING"
# Create destination folder if it doesn't exist
if (-not(Test-Path -Path $destinationFolder -PathType Container))
{
New-Item -Path $destinationFolder -ItemType Directory
}
# Get all .exe files one level deep
$files = Get-ChildItem -Path $sourceFolder -Depth 1 -Filter *.ext
foreach ($file in $files)
{
# Get standalone parent directory e.g. DIR1, DIR2
$parentFolder = Split-Path -Path (Split-Path -Path $file.FullName) -Leaf
# Create destination path with this parent directory
$destination = Join-Path -Path $destinationFolder -ChildPath $parentFolder
# Create destination parent directory if it doesn't exist
if (-not(Test-Path -Path $destination -PathType Container))
{
New-Item -Path $destination -ItemType Directory
}
# Copy file to parent directory in destination
Copy-Item -Path $file.FullName -Destination $destination
}
# Zip up destination folder
# Make sure to pass -Update for redoing compression
Compress-Archive -Path $destinationFolder -DestinationPath "OUTGOING.zip" -Update -CompressionLevel Optimal

Related

How to copy only updated or newer files

I am trying to create a PowerShell script to copy new and modified files from the source folder to the destination folder. I am able to copy the new file with the given script but also want to add the condition for the modified file also. Can anyone help me to achieve this.
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Desifolder = "C:\Users\parveen.kumar\Downloads\desi"
$GetFiles = Get-ChildItem -Path $Sourcefolder
$BackUpImagesFiles = (Get-ChildItem -Path $Desifolder).Name
foreach($image in $GetFiles)
{
$fileName = $image.Name;
if($BackUpImagesFiles -notcontains $fileName)
{
Copy-Item $image.FullName -Destination $Desifolder
}
}
You can use Get-Item to find if there is a file with that name already in the destination folder or not.
If not OR the file you found is older that the one in the source folder, copy the file.
Something like this:
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Destfolder = "C:\Users\parveen.kumar\Downloads\desi"
Get-ChildItem -Path $Sourcefolder -File | ForEach-Object {
# test if there already is a file with that name in the destination folder
$existingFile = Get-Item -Path (Join-Path -Path $Destfolder -ChildPath $_.Name) -ErrorAction SilentlyContinue
# if not existing or the existing file is older than the one in the source folder, do the copy
if (!$existingFile -or $existingFile.LastWriteTime -lt $_.LastWriteTime) {
$_ | Copy-Item -Destination $Destfolder -Force
}
}
Based on your comment, if you want to keep a copy of the file that was already in the destination folder, you can change to:
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Destfolder = "C:\Users\parveen.kumar\Downloads\desi"
Get-ChildItem -Path $Sourcefolder -File | ForEach-Object {
# test if there already is a file with that name in the destination folder
$existingFile = Get-Item -Path (Join-Path -Path $Destfolder -ChildPath $_.Name) -ErrorAction SilentlyContinue
# if a file already exists AND is older than the one in the source folder, do the copy
if ($existingFile -and $existingFile.LastWriteTime -lt $_.LastWriteTime) {
# rename the existing file first before you overwrite with a newer file from the source folder
# for demo, add the file's last modified date to its name
$newName = '{0}_{1:yyyy-MM-dd HHmmss}{2}' -f $existingFile.BaseName,
$existingFile.LastWriteTime,
$existingFile.Extension
$existingFile | Rename-Item -NewName $newName -Force
$_ | Copy-Item -Destination $Destfolder -Force
}
elseif (!$existingFile) {
$_ | Copy-Item -Destination $Destfolder -Force
}
}
Another way as you suggested is to Move the existing files into another backup folder instead of renaming them first:
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Destfolder = "C:\Users\parveen.kumar\Downloads\desi"
$BackupofDestfolder = "C:\Users\parveen.kumar\Downloads\just"
# make sure the destination and backup folders exist before trying to copy or move files there
$null = New-Item -Path $Destfolder -ItemType Directory -Force
$null = New-Item -Path $BackupofDestfolder -ItemType Directory -Force
Get-ChildItem -Path $Sourcefolder -File | ForEach-Object {
# test if there already is a file with that name in the destination folder
$existingFile = Get-Item -Path (Join-Path -Path $Destfolder -ChildPath $_.Name) -ErrorAction SilentlyContinue
# if a file already exists AND is older than the one in the source folder, do the copy
if ($existingFile -and $existingFile.LastWriteTime -lt $_.LastWriteTime) {
# move the existing file first before you overwrite with a newer file from the source folder
$existingFile | Move-Item -Destination $BackupofDestfolder -Force
$_ | Copy-Item -Destination $Destfolder -Force
}
elseif (!$existingFile) {
$_ | Copy-Item -Destination $Destfolder -Force
}
}

How can I append the creation date to all files in a folder and the subfolders in PowerShell?

I have a small script that can successfully copy all the files from folders and subfolders and append the creation time, but the files in the subfolders do not have the creation time appended to their names.
How can I append the creation date to all files in a folder and the subfolders?
My current script is:
$path = "C:\test1"
$destination = "C:\test2"
Get-ChildItem -path $path | ForEach-Object{
$newname = $_.CreationTime.toString("yyyy-MM-dd") + $_.BaseName +$_.Extension
(Copy-Item -Recurse -Path $_.FullName -Destination ( Join-Path $destination $newname))
}
You were really close, but the -Recurse switch should have been on Get-ChildItem and within the loop you need to make sure the destination subfolder paths exist.
Try
$source = "C:\test1"
$destination = "C:\test2"
Get-ChildItem -Path $source -File -Recurse | ForEach-Object {
# create the new target folderpath for the copy
$targetPath = Join-Path -Path $destination -ChildPath $_.DirectoryName.Substring($source.Length)
# make sure the target path exists, if not create it
$null = New-Item -ItemType Directory -Path $targetPath -Force
# create a new filename with CreationDate prefixed
$newName = '{0:yyy-MM-dd}{1}{2}' -f $_.CreationTime, $_.BaseName, $_.Extension
# copy the file
$_ | Copy-Item -Destination (Join-Path -Path $targetPath -ChildPath $newname) -Force
}
While you could create your own recursive method to copy files and rename them as you go, it would be easier to use Copy-Item recursively and rename the files and folders afterwards:
$Source = "src"
$Destination = "dst"
Copy-Item -Recurse $Source $Destination
foreach ($Item in (Get-ChildItem -Recurse -File $Destination)) {
Rename-Item $Item ($Item.Name + "-" + $Item.CreationTime.toString("yyyy-MM-dd"))
}

Coping files from multiple sub folders using powershell

Copy file from multiple sub-folder to another multiple sub-folder
example :
C:\Nani\Code\Relase4\database1\tables
C:\Nani\Code\Relase1\database1\tables
C:\Nani\Code\Relase2\database1\tables
C:\Nani\Code\Relase3\cycle1\database1\tables
C:\Nani\Code\Relase1\database1.02.tables
I have .sql files in above all folders and i want to copy to
C\Build\database1\tables
if database1\tables directory is not there , i have to create it too ,
$sourceFolder = "C:\Nani\Code"
$targetFolder = "C\Build"
Get-Childitem $sourceFolder -recurse -filter "*.sql" -Exclude $exclude | %{
#If destination folder doesn't exist
if (!(Test-Path $targetFolder -PathType Container)) {
#Create destination folder
New-Item -Path $targetFolder -ItemType Directory -Force
}
Copy-Item -Path $_.FullName -Destination $targetFolder -Recurse -force
}
above code is not creating sub folders in destination ,
I have kept the script very simple for your understanding and commented the sections.
Make sure you add all the validations for paths and error handling. Else if any of the files is giving any issue, then it wont proceed and will break the loop.
Script:
#Keeping all the sources in an array
$Sources = #("C:\Nani\Code\Relase4\database1\tables",
"C:\Nani\Code\Relase1\database1\tables",
"C:\Nani\Code\Relase2\database1\tables",
"C:\Nani\Code\Relase3\cycle1\database1\tables",
"C:\Nani\Code\Relase1\database1.02.tables")
$Destination="C\Build\database1\tables\"
#Iterating each source folder
foreach($source in $sources)
{
#Getting all the sql files under an iteration folder recursively
$files=Get-ChildItem -Path $source -Filter "*.sql" -Recurse
#Iterating all the files underneath a single source folder
foreach ($file in $files)
{
#Copying the files for a single folder to the destination
Copy-Item $file.PSPath -Destination ("$Destination" + ($file.PSParentPath | Split-Path -Leaf) + '_' + $file)
}
}
Hope it helps.
Try this, I am creating each folder first before copying files into it.
$sourceFolder = "C:\Nani\Code"
$targetFolder = "C:\Build"
$sources = Get-Childitem $sourceFolder -recurse -filter "*.sql" -Exclude $exclude | Select FullName, DirectoryName
foreach ($source in $sources)
{
$Releasepath = [regex]::match($source.DirectoryName,'C:\\Nani\\Code\\Release\d').Value
$split = $Releasepath.Replace("\","\\")
$targetfolderLeaf = $source.DirectoryName -split $split | select -Last 1
$targetfolderpath = $targetFolder+$targetfolderLeaf
if (!(Test-Path $targetfolderpath -PathType Container)) {
#Create destination folder
New-Item -Path $targetfolderpath -ItemType Directory -Force
}
Copy-Item -Path $source.FullName -Destination $targetfolderpath -Recurse -force
}

Merge two directories keeping any files with same name

I am looking for some help to create a PowerShell script to merge or copy one directory to another that the destination directory has files with the same name as the source.
I need to keep both, the script can append a number to the source file if it has a file of duplicate name in the destination.
Here is a sample script that deals with one file, but I need to set a directory and let it loose recursively on the entire directory.
$SourceFile = "C:\Temp\File.txt"
$DestinationFile = "C:\Temp\NonexistentDirectory\File.txt"
if ((Test-Path $DestinationFile) -eq $false) {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile
try this
$SourceDir = "C:\Temp"
$DestinationDir = "C:\Temp2\NonexistentDirectory"
#create dir if not exists (dont remove if exist)
New-Item -ItemType Directory -Path $DestinationDir -Force
#get list files destination dir
$DestinationFiles=gci $DestinationDir -File
#loop on file source and create newname for copy while name exist already
gci $SourceDir -File | %{
$counter=0
$name=$_.Name
while ($name -in $DestinationFiles.Name)
{
$counter++;
$name="{0}_{1:d6}{2}" -f $_.BaseName, $counter, $_.Extension
}
Copy-Item -Path $_.FullName -Destination "$DestinationDir\$name"
}

PowerShell Compress-Archive By File Extension

How can I use the PowerShell 5.0 Compress-Archive cmdlet to recursively take any .config files in a directory and zip them up while maintaining the directory structure. Example:
Directory1
Config1.config
Directory2
Config2.config
The aim is a single zip file also containing the above directory structure and only config files.
I would suggest copying the files to a temporary directory and compress that. Ex:
$path = "test"
$filter = "*.config"
#To support both absolute and relative paths..
$pathitem = Get-Item -Path $path
#If sourcepath exists
if($pathitem) {
#Get name for tempfolder
$tempdir = Join-Path $env:temp "CompressArchiveTemp"
#Create temp-folder
New-Item -Path $tempdir -ItemType Directory -Force | Out-Null
#Copy files
Copy-Item -Path $pathitem.FullName -Destination $tempdir -Filter $filter -Recurse
#Get items inside "rootfolder" to avoid that the rootfolde "test" is included.
$sources = Get-ChildItem -Path (Join-Path $tempdir $pathitem.Name) | Select-Object -ExpandProperty FullName
#Create zip from tempfolder
Compress-Archive -Path $sources -DestinationPath config-files.zip
#Remove temp-folder
Remove-Item -Path $tempdir -Force -Recurse
}