I have three directories:
1. RFC
2. Source
3. Backup
RFC contains files and folders(that contain files) that I need to replace in the source folder but before I replace/move files I need to backup the files I'm replacing from source to the backup folder.
I have wrote the following code, which compares RFC and Source and copies the files to backup, but it doesn't copy sub directories. I want it to move files within the sub directories as well with the same folder structure as Source. And once the copy of the files is done. I want to move files from RFC to Source.
Please any help would be highly appreciated.
$source = "C:\Scripts\Source\"
$backup = "C:\Scripts\Destination\"
$rfc_dir = "C:\Scripts\RFC000001234\"
$folder1Files= dir $source
$folder2Files= dir $rfc_dir
compare-object $folder1Files $folder2Files -property name -includeEqual -excludeDifferent | ForEach-object {
copy-item "$source\$($_.name)" -Destination "$backup" -Force -recurse
}
UPDATE
TL;DR -- the script
$RFC_Folder = 'c:\scripts\rfc'
$SOURCE_Folder = 'c:\scripts\source'
$BACKUP_Folder = 'c:\scripts\backup'
$rfc = get-ChildItem -File -Recurse -Path $RFC_Folder
$source = get-ChildItem -File -Recurse -Path $SOURCE_Folder
compare-Object -DifferenceObject $rfc -ReferenceObject $source -ExcludeDifferent -IncludeEqual -Property Name -PassThru | foreach-Object {
# copy SOURCE to BACKUP
$backup_dest = $_.DirectoryName -replace [regex]::Escape($SOURCE_Folder),$BACKUP_Folder
# create directory, including intermediate paths, if necessary
if ((test-Path -Path $backup_dest) -eq $false) { new-Item -ItemType Directory -Path $backup_dest | out-Null}
copy-Item -Force -Path $_.FullName -Destination $backup_dest
#copy RFC to SOURCE
$rfc_path = $_.fullname -replace [regex]::Escape($SOURCE_Folder),$RFC_Folder
copy-Item -Force -Path $rfc_path -Destination $_.FullName
}
The explanation:
Given the OPs comment below on ROBOCOPY not being preferable I've updated the answer.
Your posted script is basically on the right track, however instead of using just $backup you have to get a little fancy with the -Destination parameter. You don't want the -Destination to be the static path c:\scripts\backup you want it to update based on where the source file actually was. For example if the file was in c:\scripts\source\subdir1\subdir2, you'd want -Destination to be c:\scripts\backup\subdir1\subdir2.
$_.FullName will be the path to the $SOURCE_Folder as it was used as the -ReferenceObject. It's getting manipulated stringwise to create the desired RFC and Backup paths. The [regex]::Escape static method is being used because the -replace operator does a regular expression operation on the strings, and several characters in paths need to be escaped (the slash, mainly). All it's doing is turning c:\scripts\source into a regular expression escaped version: c:\\scripts\\source
The if construct is used because copy-Item doesn't create intermediate directories, but new-Item does.
Depending on your specifics this might work as is, but you may have to alter it. For example if somehow a directory can end up in RFC that wasn't in SOURCE, this wouldn't catch that. It also won't catch any empty directories that are in SOURCE and RFC, if that's important.
It may be that PowerShell isn't the best tool for this job, depending on some other factors. As I understand it you have Source, RFC and Backup folders. The RFC folder will contain changes that need to be committed to Source, but before you do that you want to backup Source to Backup. If the folder structures are all similar between them, then perhaps the command line tool ROBOCOPY could do what you need?
If some of my assumptions are correct you'd first mirror the Source folder with the Backup folder. This would contain the pre-changed files. Then you would mirror the RFC folder to Source, this would commit any changed files/folders from RFC to the Source folder. An example (this is a batch file):
REM Mirror the Source folder to Backup
ROBOCOPY C:\Scripts\Source C:\Scripts\Backup /MIR
REM Mirror the RFC folder to Source
ROBOCOPY C:\Scripts\RFC C:\Scripts\Source /MIR
At the end of all this your Source folder would be an exact replica of whatever the RFC folder looked like. If RFC isn't a full copy of Source, but rather a partial copy, then you wouldn't want to use the mirror switch, /MIR, as it would destroy anything in Source that wasn't in RFC.
Browse around ROBOCOPY /? for some of its other switches, it's got a few interesting ones for logging if you want to build in some auditing capability. Also, make extra sure to test this in a test environment. Misuse of ROBOCOPY with a /MIR switch might make you a very sad camper.
Related
i'm trying to copy file from source to destination any verify if file copied or not.
But the problem is if i make changes inside the source file which was copied earlier then destination file not getting override when i execute the code again. Also i want log file each time files are copied.
Files in folder:- .csv, .log, .png, .html
$source="C:\52DWM93"
$destination="C:\Temp\"
Copy-Item -Path $source -Destination $destination -Force
$ver=(Get-ChildItem -file -path $destination -Recurse).FullName | foreach {get-filehash $_ -Algorithm md5}
If($ver)
{Write-Host "ALL file copied"}
else
{Write-Host "ALL file not copied"}
If you copy directories like this you need the -Recurse switch for Copy-Item. Without it you're not going to copy anything except the directory itself.
You can of course also use Get-ChildItem with whatever filter or Recurse flag you care about and pipe that into Copy-Item.
Use the *-FileCatalog cmdlets for verification.
I am trying to copy all the files in a directory that contains many subfolders into a single separate folder. When the code is run again, rather than replacing each file in the destination folder, it should skip files that have the same timestamp and only replace those that are older.
I have used robocopy to skip the copying of files that are of the current version/older in the destination folder. However, robocopy only copies the entire directory along with its folder structure so I am unable to obtain the desired folder with a list of all the files from the source.
I have also used get child-item and then copy-item. However, although this is able to get rid of the folder structure, it overwrites each file for each iteration and is thus time-consuming.
So what I want is to combine the capabilities of robocopy and copy-item. Note that there are no specific pattern to the files that I am to copy. It is simply to COPY each file in the subdirectories that are EITHER of a NEWER version or NON-existing into a single folder.
#For copying and ease of updating destination folder
robocopy /purge /np /S /xo 'source' 'destination'
#To copy items into the destination folder without keeping folder structure
Get-ChildItem -Path 'source' -Recurse -File | Copy-Item -Destination 'destination'
Was unable to combine both, So I am stuck with using the 'copy-item' code, which is quite time consuming when copying/updating large amounts of files.
The purpose of robocopy is to preserve the folder structure. If you want to mangle subfolders robocopy is not the right tool. Use the Get-ChildItem approach, group the results by file name, sort each group by date, pick the most recent file from each group, and copy it if the corresponding destination file either doesn't exist or is older.
Something like this should do what you want:
Get-ChildItem -Path 'C:\source' -Recurse -File |
Group-Object Name |
ForEach-Object {
$src = $_.Group | Sort-Object | Select-Object -Last 1
$dst = Join-Path 'C:\destination' $src.Name
if (-not (Test-Path $dst) -or ($src.LastWriteTime -gt (Get-Item $dst).LastWriteTime)) {
$src | Copy-Item -Destination $dst
}
}
I have a set of projects that involve a mix of project-specific files plus common files. I'm trying to copy contents from two different folders -- a project-specific folder, and a common folder -- into a single folder named for the project. I also want to retain any folder hierarchies from the original folders.
For example, some paths to the common files:
src\Common\PackageAssets\logo1.jpg
src\Common\PackageAssets\logo2.jpg
And example paths to project-specific files:
src\Projects\ProjectA\PackageFiles\readme.txt
src\Projects\ProjectA\PackageFiles\scale-100\projA.png
The desired result after copying would be:
bld\ProjectA\pkgFiles\logo1.png
bld\ProjectA\pkgFiles\logo2.png
bld\ProjectA\pkgFiles\readme.txt
bld\ProjectA\pkgFiles\scale-100\projA.png
What I'm using is this:
[string]$pkgContentPath = "bld\$project\pkgFiles"
# copy common files
Copy-Item -Path .\src\Common\PackageAssets -Recurse -Destination $pkgContentPath
# copy project-specific files
Copy-Item -Path .\src\Projects\ProjectA\PackageFiles\ -Recurse -Destination $pkgContentPath
But instead of the expected results, all the files are ending up in an extra level of subfolder:
bld\ProjectA\pkgFiles\PackageAssets\logo1.png
bld\ProjectA\pkgFiles\PackageAssets\logo2.png
bld\ProjectA\pkgFiles\PackageFiles\readme.txt
bld\ProjectA\pkgFiles\PackageFiles\scale-100\projA.png
I'm stumped. I can't figure out how to get rid of the extra subfolder layer. I tried using Get-ChildItem piping to Copy-Item, but then the subfolder hierarchies were lost.
In a .bat file, this works:
xcopy src\Common\PackageAssets\* bld\%project\pkg_contents /s
xcopy src\Projects\%project\PackageFiles bld\%project\pkg_contents /s
I guess I could use xcopy, but surely there must be a way to do this using cmdlets.
The behavior you describe is a known problem as of Windows PowerShell v5.1 / PowerShell Core v6.2.0-preview.2, unfortunately:
In short, the behavior of Copy-Item regrettably depends on whether the target directory happens to exist already or not:
If the target dir. exists, it is not the source directory's content that is copied, but the source dir. as a whole, to a subdirectory of the target directory named for the source dir.
The workaround is already mostly spelled out in PetSerAl's helpful comment on the question:
Before copying, make sure that the target directory exists.
Append \* to the source path to explicitly target the contents of the source dir.
Also specify -Force, so as to ensure that hidden files and directories are also copied.
$pkgContentPath = "bld\$project\pkg_contents"
# Make sure the target dir. exists.
# (-Force leaves an existing dir alone and otherwise creates it.)
New-Item -Force -Type Directory $pkgContentPath
# IF desired, clear out the pre-existing directory content.
# !! Beware of Remove-Item's intermittent failures - see below.
# Remove-Item -Force $pkgContentPath\* -Recurse.
# Copy with \* appended to the source paths and -Force added:
#
# copy common files
Copy-Item -Recurse -Force -Path .\src\Common\PackageAssets\* $pkgContentPath
# copy project-specific files
Copy-Item -Recurse -Force -Path .\src\Projects\ProjectA\PackageFiles\* $pkgContentPath
A note re use of Remove-Item -Recurse to clear out preexisting destination-directory content, if needed: Regrettably, Remove-Item can fail on occasion and you cannot predict when that happens - see this answer for a robust alternative.
I have a PS script which Zips up the previous months logs and names the zip file FILENAME-YYYY-MM.zip
This works
What I now want to do is copy these zip files off to a network share but keeping some of the folder structure. I currently a folder structure similar to the following;
C:\Folder1\
C:\Folder1\Folder2\
C:\Folder1\Folder3\
C:\Folder1\Folder4\Folder5\
There are .zip files in every folder below c:\Folder1
What I want is for the script to copy files from c:\folder1 to \\networkshare but keeping the folder structure, so I should have 3 folders and another subfolder in folder4.
Currently I can only get it to copy the whole structure so I get c:\folder1\... in my \\networkshare
I keep running into issues such as the new folder structure doesn't exist, I can't use the -recurse switch within the Get-ChildItem command etc...
The script I have so far is;
#This returns the date and formats it for you set value after AddMonths to set archive date -1 = last month
$LastWriteMonth = (Get-Date).AddMonths(-3).ToString('MM')
#Set destination for Zip Files
$DestinationLoc = "\\networkshare\LogArchive\$env:computername"
#Source files
$SourceFiles = Get-ChildItem C:\Sourcefiles\*.zip -Recurse | where-object {$_.lastwritetime.month -le $LastWriteMonth}
Copy-Item $SourceFiles -Destination $DestinationLoc\ZipFiles\
Remove-Item $SourceFiles
Sometimes, you just can't (easily) use a "pure PowerShell" solution. This is one of those times, and that's OK.
Robocopy will mirror directory structures, including any empty directories, and select your files (likely faster than a filter with get-childitem will). You can copy anything older than 90 days (about 3 months) like this:
robocopy C:\SourceFiles "\\networkshare\LogArchive\$($env:computername)\ZipFiles" /E /IS /MINAGE:90 *.zip
You can specify an actual date with /MINAGE too, if you have to be that precise.
How about Copy-Item "C:\SourceFiles\" -dest $DestinationLoc\ZipFiles -container -recurse? I have tested this and have found that it copies the folder structure intact. If you only need *.zip files, you first get them, then for each you call Resolve-Path with -Relative flag set and then add the resultant path into Destination parameter.
$oldloc=get-location
Set-Location "C:\SourceFiles\" # required for relative
$SourceFiles = Get-ChildItem C:\Sourcefiles\*.zip -Recurse | where-object {$_.lastwritetime.month -le $LastWriteMonth}
$SourceFiles | % {
$p=Resolve-Path $_.fullname -relative
copy-item $_ -destination "$DestinationLoc\ZipFiles\$p"
}
set-location $oldloc # return back
I have a folder named source. It's structure is like the following.
source\a.jpg
source\b.jpg
source\c.xml
source\d.ps1
source\subdir1\a.xml
source\subdir2\b.png
source\subdir3\subsubdir1\nothing.img
I want to list all the relative paths of folders, sub-folders and files in a text file say, out.txt. For above the output I expect is:
source\a.jpg
source\b.jpg
source\c.xml
source\d.ps1
source\subdir1\a.xml
source\subdir2\b.png
source\subdir3\subsubdir1\nothing.img
source\subdir1
source\subdir2
source\subdir3
source\subdir3\subsubdir1
You can see that the output includes individual folders and sub-folders too.
Note: I am in a folder just outside the source folder. I mean for example I am in fold folder which contains source folder -> fold/source but if your solution includes putting the script inside the source folder, thats fine too. Both solutions are fine. This may be easy but I am not familiar with powershell but can at least run scripts from it if given.
EDIT1: Okay, in the "duplicate question" the answer is for relative paths of individual files. But I also want the folders and sub-folders.
EDIT2: Okay, I wrote a command:
(gci -path source -recurse *.|Resolve-path -relative) -replace "","" | out-file -FilePath output.txt -Encoding ascii
Now, this command gives me the relative name of only subdirectory inside the source(in the actual source folder of mine with a different name; source is a dummy name obviously!). What should I change in this code to get other names of files inside the subdirectory in source.
This is clean one line answer,no loops on my part to write, which does the job perfectly. I produced it by luck "playing around" a bit.
(gci -Path source -Recurse *.*|Resolve-Path -Relative) -replace "\.","" |
Out-File -FilePath output.txt -Encoding ascii
Not very clean but this might be an option.
(Get-ChildItem -Recurse -Path "C:\ABC\source\").FullName|ForEach{[Regex]::Replace($_,'^C:\\ABC\\','')}
I would probably do something like this:
$source = 'C:\path\to\source'
$parent = [IO.Path]::GetDirectoryName($source) -replace '\\+$'
$pattern = '^' + [regex]::Escape($parent) + '\\'
Get-ChildItem -Path $source -Recurse | % { $_.FullName -replace $pattern }