Copy-Items: Copying the same folder more than once - powershell

I try to copy the files and folder that is last access 1 year ago and this is my script:
$Source = "\\UNC\Path\Folder\"
$Dest = "\\UNC\Path2\Folder\1"
$Get = Get-ChildItem $Source -Recurse | where {
$_.LastAccessTime -ge (Get-Date).AddMonths(-12).ToString("yyyy-MM-dd")
}
$Get | Copy-Item -Destination $Dest -Recurse
The script works except it copies the files and folder more than once.
For example, it will copy \\UNC\Path\Folder\a\b\File1.txt to both:
\\UNC\Path2\Folder\1\a\b\File1.txt
\\UNC\Path2\Folder\a\b\File1.txt
Note it skips the folder called 1 and puts it directly under Folder.
Now the File1.txt have been copied twice and it's the same file, just different dest. location.
I have Googled and search this forum but I haven't found anything. Any idea what it might be?

I used RoboCopy as #TessellatingHeckler suggested and it work great!
Robocopy.exe \\Source\folder\Folder1 \\dest\folder\folder\1 /MAXLAD:365 /e /copyall /log:C:\Logs\log.txt
Robocopy source: https://social.technet.microsoft.com/wiki/contents/articles/1073.robocopy-and-a-few-examples.aspx#Copy_all_content_including_empty_directory
/maxlad: Specifies the maximum last access date (excludes files
unused since N).
/minlad: Specifies the minimum last access date (excludes files
used since N) If N is less than 1900, N specifies the number of days.
Otherwise, N specifies a date in the format YYYYMMDD

Related

Copy and rename a file every minute

So I am trying to copy files from a folder to another one. The files in this folder are overwritten every minute by another program. I want to get a copy of each file every minute before it gets overwritten and save it somewhere else. See example structure below:
Folder 1 # gets overwritten every minute
a.txt
a_backup.txt
Folder 2
a1.txt
a1_backup.txt
a2.txt
a2_backup.txt
a3.txt
a3_backup.txt
a4.txt
a4_backup.txt
It would be even better if the files in Folder 2 would contain the date and time of when they were copied in their names.
I came up with the following:
$Source = 'C:\Users\Source'
$Destination = 'C:\Users\Target'
Do{
Copy-Item $Source\* -Destination $Destination
sleep -s 59
} while($true)
However, this does not do the job completely as I am only copying the file once and then copy the same file again when it's overwritten...
Any help is warmly welcome!
New on giving answers but here's my proposal.
Get Content , and out to another file with current time as of writing maybe? Of course include your loop around it
Get-Content C:\log.txt | Out-File "Log.$([System.Math]::Round((date -UFormat %s),0)).txt""
Get-ChildItem -Path $source | % { copy-item $_.FullName -Destination "$Destination\$((Get-Date).ToString("MMddyyyy-hhmmss"))$($_.Name)" }
This statement will take care of it but what it will not do is hide the exceptions and failures you will get while the file is being written to.
$_.FullName includes the full path.. can be used as source
$_.Name gives you the filename only (without the path)
(Get-Date).ToString("MMddyyyy-hhmmss") gives you the date in format specified in ToString(). Since the file is updated every minute, you will need to include the minutes and seconds in your filename as well.

Copying all Files in Subdirectories to a Single Folder with Robocopy Updating Capabilities

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
}
}

Copy file from directories with today's date to another location

AD Manager Plus generates reports hourly to a time stamped file path and I would like to copy these files to another location - overwriting the existing file. I will then schedule the script to run hourly after the files have been generated. Unfortunately the location the reports are extracted to cannot be modified and it creates date & time stamped folders.
Example:
C:\ADManager Plus\audit-data\reports\16042019\DailyTrue-Up01-55-07\Real Last Logon.xls
C:\ADManager Plus\audit-data\reports\ddmmyyyy\DailyTrue-Uphh-mm-ss\Real Last Logon.xls
I thought the easiest approach would be to:
Get the last modified folder in the Reports Folder - eg Apr162019
Get the last modified folder in the Apr162019 Folder - eg DailyTrue-Up01-55-07
Filter for the Real Last Logon.xls spreadsheet in folder DailyTrue-Up01-55-07
$Path = "C:\ADManager Plus\audit-data\reports"
$DestinationPath = "\\domain\networkshare\Reports\"
Get-ChildItem -Path $Path -Directory | ForEach-Object {
Get-ChildItem -Path "$Path\$_" -File -Filter "Real Last Logon.xlsx" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 |
Copy-Item -Force -Destination (New-Item -Force -Type Directory -Path (Join-Path $DestinationPath ($_.FullName.Replace("$Path\", ''))))
}
The code we have seems to copy all folders to the location and can't look in multiple directories.
I got a feeling we are approaching this wrong, Can anyone suggest the best way to achieve this? There are few posts online that explain how to retrieve files from time stamped folders.

Powershell Copy files and folders

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

recursing folders and renaming files with create date added to filename

I am very much a newbie to powershell but have figured out some of what I need for a project at work. We have test result files (PDF, XLS. TXT, etc) in folders relating to test locations. Most folders have subfolders with files. The boss has mandated that the test date must be appended to the end of the file name (test_results.pdf -> test_results - 2014-05-06.pdf).
My code does get the creation date and appends it, however it only works on the folders in the source folder, also it is appending the creation date of the folder. I don't mind the date in the folder name if all the files ends up in the correct location. The files in the source subfolders are written to the new sub but without creation date appended.
Any help is greatly appreciated. Here's my code:
$SourceDir= 'C:\modem\'
$targetDir = 'C:\test2\'
set-location -path $sourceDir
$files = get-childitem -recurse
foreach ($file in $files)
{
[string]$strippedFileName =[io.path]::GetFileNameWithoutExtension($file);
[string]$extension = [io.Path]::GetExtension($file);
[string]$crtime=$file.CreationTime.toString(' - yyyy-MM-dd');
[string]$sourceFilePath = $file.DirectoryName;
[string]$DestinationFile = $targetDir + $sourcefilepath.trimstart($sourceDir) + "\" + $strippedFileName +$crtime + $extension;
Copy-Item $file.Fullname -Destination $DestinationFile -recurse -Force
}
Thank you,
tom
It sounds like it doesn't work for the files in the subfolders because you're creating subfolders in the destination directory with different names (date appended), but when you try to copy the files, you're using the original subfolder names in the path to the destination, so you're trying to copy them to locations that don't exist.
If that's the case, you might be wondering, then why do the files from the source subfolders get copied to the corresponding renamed subfolders, but without the date appended? Simple: because if you Copy-Item -Recurse a folder, all the contents get copied. The files in the subfolders aren't being copied individually by your foreach loop; they're being copied all at once by the last line of the loop each time you create a new (renamed) subfolder.
You can solve both problems simultaneously by simply exculding directories from the results of Get-ChildItem.
In PowerShell 1 and 2:
$files = Get-Childitem -Recurse | ?{$_.PSIsContainer}
In PowerShell 3 and up:
$files = Get-Childitem -Recurse -File
Also, it looks like you're coming from a background of .NET programming in a strongly type language (I'm guessing C# based on your accent). PowerShell is dynamically typed; there's no need (and no advantage) to casting all your variables as strings. The assigments will make them strings. Also, you don't need [IO.Path] to get the parts of the filename; they're attributes of the FileInfo objects returned by Get-ChildItem.
You can write you loop like this:
foreach ($file in $files)
{
$strippedFileName = $file.BaseName;
$extension = $file.Extension;
$crtime=$file.CreationTime.toString(' - yyyy-MM-dd');
$sourceFilePath = $file.DirectoryName;
$DestinationFile = $targetDir + $sourcefilepath.TrimStart($sourceDir) + "\" + $strippedFileName +$crtime + $extension;
Copy-Item $file.FullName -Destination $DestinationFile -Recurse -Force
}