Copy folder structure and content of specific folders - powershell

So I've found xcopy super helpful to copy entire folder structures. But I also need to copy the contents of specific folders into the new directories as well.
For example:
1. C:\OriginalDir
- \This
* \Test
- \That
* \Test
- \Other
I can use: xcopy C:\OriginalDir C:\TempDir /e /t to copy the entire structure of the C:\OriginalDir. However, I also need to copy the contents of both \Test folders into the new directory as well. I'm fairly new to xcopy and I've also looked into robocopy. Is there a way to do this? I'm trying to accomplish this in powershell and thought about iterating through the folder structure, but that still doesn't store the parent folder structure when I finally reach the Test folder.
Thanks.

Have you tried robocopy - for example:
$source = "C:\Your\Source\Directory"
$dest = "C:\Your\Destination\Directory"
robocopy $source $dest /e
The 'e' switch will copy subdirectories and their contents (including empty subdirectories).
If you wanted to exclude the \Other directory (it's not entirely clear from your question), you could do the following:
robocopy $source $dest /e /xf *
(This just copies the directory structure with no files copied)
robocopy $source $dest /XD C:\Other /e
(This copies files, but excludes the named directories)
You can find more information here:
https://technet.microsoft.com/en-GB/library/cc733145.aspx
Edit:
In order to only copy directories beginning with 'Test', you could do the following:
$exclude = gci C:\OriginalDir -ad | ?{ $_.Name -notlike 'Test*'
robocopy $source $dest /XD $exclude /e
If your folder structure is more than one level deept, you could use the -recurse switch on Get-Childitem

Thanks to Steve for getting me started and getting me thinking about this correctly. Ended up scripting it out manually without using RoboCopy or Xcopy as I could not get them to work exactly how I wanted to.
$target = "C:\\TestTemp"
foreach($item in (Get-ChildItem "C:\\OriginalDir\\This" -Recurse)){
if ($item.PSIsContainer -and ($item.Name -eq "obj" -or $item.Name -eq "bin")){
$tempPath = $target
$path = $item.FullName
$trimmed = $path.TrimStart("C:\\OriginalDir")
$pieces = $trimmed.Split("\\");
foreach($piece in $pieces){
$tempPath = "$tempPath\$piece"
New-Item -ItemType Directory -Force -Path "$tempPath"
if($piece -eq "Test" -or $piece -eq "Temp"){
Copy-Item -path $path\** -Destination $tempPath -Recurse -force
}
}
}
}

Related

Powershell: Find Folders with (Name) and Foreach Copy to Location Preserve Directory Structure

Got another multi-step process I'm looking to streamline. Basically, I'm looking to build a Powershell script to do three things:
Get-Childitem to look for folders with a specific name (we'll call it NAME1 as a placeholder)
For each folder it finds that has the name, I want it to output the full directory to a TXT file (so that in the end I wind up with a text file that has a list of the results it found, with their full paths; so if it finds folders with "NAME1" in five different subdirectories of the folder I give it, I want the full path beginning with the drive letter and ending with "NAME1")
Then I want it to take the list from the TXT file, and copy each file path to another drive and preserve directory structure
So basically, if it searches and finds this:
D:\TEST1\NAME1
D:\TEST7\NAME1
D:\TEST8\NAME1\
That's what I want to appear in the text file.
Then what I want it to do is to go through each line in the text file and plug the value into a Copy-Item (I'm thinking the source directory would get assigned to a variable), so that when it's all said and done, on the second drive I wind up with this:
E:\BACKUP\TEST1\NAME1
E:\BACKUP\TEST7\NAME1
E:\BACKUP\TEST8\NAME1\
So in short, I'm looking for a Get-Childitem that can define a series of paths, which Copy-Item can then use to back them up elsewhere.
I already have one way to do this, but the problem is it seems to copy everything every time, and since one of these drives is an SSD I only want to copy what's new/changed each time (not to mention that would save time when I need to run a backup):
$source = "C:\"
$target = "E:\BACKUP\"
$search = "NAME1"
$source_regex = [regex]::escape($source)
(gci $source -recurse | where {-not ($_.psiscontainer)} | select -expand fullname) -match "\\$search\\" |
foreach {
$file_dest = ($_ | split-path -parent) -replace $source_regex,$target
if (-not (test-path $file_dest)){mkdir $file_dest}
copy-item $_ -Destination $file_dest -force -verbose
}
If there's a way to do this that wouldn't require writing out a TXT file each time I'd be all for that, but I don't know a way to do this the way I'm looking for except a Copy-Item.
I'd be very grateful for any help I can get with this. Thanks all!
If I understand correctly, you want to copy all folders with a certain name, keeping the original folder structure in the destination path and copy only files that are newer than what is in the destination already.
Try
$source = 'C:\'
$target = 'E:\BACKUP\'
$search = 'NAME1'
# -ErrorAction SilentlyContinue because in the C:\ disk you are bound to get Access Denied on some paths
Get-ChildItem -Path $source -Directory -Recurse -Filter $search -ErrorAction SilentlyContinue | ForEach-Object {
# construct the destination folder path
$dest = Join-Path -Path $target -ChildPath $_.FullName.Substring($source.Length)
# copy the folder including its files and subfolders (but not empty subfolders)
# for more switches see https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
robocopy $_.FullName $dest  /XO /S /R:0
}
If you don't want console output of robocopy you can silence it by appending 2>&1, so neither stdout nor stderr is echoed
If you want to keep a file after this with both the source paths and the destinations, I'd suggest doing
$source = 'C:\'
$target = 'E:\BACKUP\'
$search = 'NAME1'
$output = [System.Collections.Generic.List[object]]::new()
# -ErrorAction SilentlyContinue because in the C:\ disk you are bound to get Access Denied on some paths
Get-ChildItem -Path $source -Directory -Recurse -Filter $search -ErrorAction SilentlyContinue | ForEach-Object {
# construct the destination folder path
$dest = Join-Path -Path $target -ChildPath $_.FullName.Substring($source.Length)
# add an object to the output list
$output.Add([PsCustomObject]#{Source = $_.FullName; Destination = $dest })
# copy the folder including its files and subfolders (but not empty subfolders)
# for more switches see https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
robocopy $_.FullName $dest  /XO /S /R:0
}
# write the output to csv file
$output | Export-Csv -Path 'E:\backup.csv' -NoTypeInformation

Place files with prefix in folder with number

I have batch changed multiple files that all start with a prefix of a folder where I need them in.
The files are located on another location, like a folder on the desktop.
For example:
101AA0001.dat
101AA0002.dat
102AA0001.dat
102AA0002.dat
The destination folder will for example be:
C:\destfolder\101\ or C:\destfolder\102\
Files starting with 101 need to go in the 101 folder and the files starting with 102 go to folder 102.
I can find some scripts that creates the folder based on the filename. But in this situation the folders already exist. I also know for sure the files don't exist, so I don't have to overwrite files or something.
I guess it is easy for the people that know PowerShell very well, but I don't know how to do this. Can someone please help me? This can save me a lot of time.
I have tried to move the files with the following rule:
Move-Item -Path C:\Users\Username\Desktop\test*.dat -Destination C:\Users\Username\Desktop\test2\ -include "*.dat"
But it copies the whole folder except for the files.
You can do that quite easily with code like below:
$sourceFolder = Join-Path -Path $env:USERPROFILE -ChildPath 'Desktop'
$destination = 'C:\destfolder'
Get-ChildItem -Path $sourceFolder -File -Filter '*.dat' | ForEach-Object {
$targetFolder = Join-Path -Path $destination -ChildPath $_.Name.Substring(0, 3)
# if the target folder does not exist yet, create it
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
$_ | Move-Item -Destination $targetFolder -WhatIf
}
The -WhatIf switch shows what would happen in the console without actually performing the move. If you are satisfied with what is output, remove that switch.
This will take all files that end in ".dat" from the $Source folder into a subfolder inside the $DestinationRoot named for the first three characters of the ".dat" file.
$Source = "C:\Users\Username\Desktop"
$DestinationRoot = "C:\Users\Username\Desktop\test2"
$Filelist = Get-ChildItem -Path $Source -Filter "*.dat" -File
foreach ($File in $Filelist){ $DestinationFolder = $File.Name.Substring(0,3)
$FinalPath = "$DestinationRoot\$DestinationFolder"
Move-Item -Path $File.Fullname -Destination $FinalPath -Whatif }
Remove the -Whatif when you're ready to run it for real.
This doesn't handle folder creation and should error out if the file already exists in the target location so it won't accidentally overwrite anything.

How to overwrite files with Copy-Item in PowerShell

I am trying to copy content of a folder, but there are two files which I would like to exclude. The rest of all the content should be copied to a new location and existing content on that new location should be overwritten.
This is my script. It works fine if my destination folder is empty, but if I have files and folder, it doesn't overwrite them.
$copyAdmin = $unzipAdmin + "/Content/*"
$exclude = #('Web.config','Deploy')
Copy-Item -Path $copyAdmin -Destination $AdminPath -Exclude $exclude -Recurse -force
As I understand Copy-Item -Exclude then you are doing it correct. What I usually do, get 1'st, and then do after, so what about using Get-Item as in
Get-Item -Path $copyAdmin -Exclude $exclude |
Copy-Item -Path $copyAdmin -Destination $AdminPath -Recurse -force
Robocopy is designed for reliable copying with many copy options, file selection restart, etc.
/xf to excludes files and /e for subdirectories:
robocopy $copyAdmin $AdminPath /e /xf "web.config" "Deploy"
How about calling the .NET Framework methods?
You can do ANYTHING with them... :
[System.IO.File]::Copy($src, $dest, $true);
The $true argument makes it overwrite.

Copy file with relative path

I would like to copy all files of a certain type from a certain sub-directory with their relative path from that sub-directory to another directory with the relative path intact. e.g.:
Source sub-dir:
c:\temp\sourcedirectory
Source files:
c:\temp\sourcedirectory\tonymontana\fileOne.txt
c:\temp\sourcedirectory\poker\fileTwo.txt
Target dir:
c:\temp\targetdirectory
Desired result:
c:\temp\targetdirectory\tonymontana\fileOne.txt
c:\temp\targetdirectory\poker\fileTwo.txt
So far I've come up with:
Set-Location $srcRoot
Get-ChildItem -Path $srcRoot -Filter $filePattern -Recurse |
Resolve-Path -Relative |
Copy-Item -Destination {Join-Path $buildroot $_.FullName}
However, this "everything is an object" à la PowerShell is beating me down (at least that's what I suspect). I.e. the files gets copied, but without their relative path.
Anyone who could enlighten me a bit?
Don't bother with PowerShell cmdlets for this, simply use robocopy:
robocopy C:\temp\sourcedirectory C:\temp\targetdirectory *.txt /s
You can try this:
$srcroot = "c:\temp\sourcedirectory"
$builroot= "c:\temp\targetdirectory"
gci -path $srcroot -filter $filepattern -recurse |
% { Copy-Item $_.FullName -destination ($_.FullName -replace [regex]::escape($srcroot),$builroot) }
Try this:
Copy-item $srcRoot -destination $destination -recurse
To prevent copying the folder itself i.e. creating
c:\temp\targetdirectory\sourcedirectory
Change into the source folder, then use a wildcard instead of the folder as the source:
cd C:\temp\sourcedirectory\
Copy-item * -destination c:\temp\targetdirectory -recurse`

Need a script to publish build output to a staging server

I am trying to write a PowerShell script that will copy a subset of files from a source folder and place them into a target folder. I've been playing with "copy-item" and "remove-item" for half a day and cannot get the desired or consistent results.
For example, when I run the following cmdlet multiple times, the files end up in different locations?!?!:
copy-item -Path $sourcePath -Destination $destinationPath -Include *.dll -Container -Force -Recurse
I've been trying every combination of options and commands I can think of but can't find the right solution. Since I'm sure that I'm not doing anything atypical, I'm hoping someone can ease my pain and provide me with the proper syntax to use.
The source folder will contain a large number of files with various extensions. For example, all of the following are possible:
.dll
.dll.config
.exe
.exe.config
.lastcodeanalysisissucceeded
.pdb
.Test.dll
.vshost.exe
.xml
and so on
The script needs to only copy .exe, .dll and .exe.config files excluding any .test.dll and .vshost.exe files. I also need the script to create the target folders if they don't already exist.
Any help getting me going is appreciated.
try:
$source = "C:\a\*"
$dest = "C:\b"
dir $source -include *.exe,*.dll,*.exe.config -exclude *.test.dll,*.vshost.exe -Recurse |
% {
$sp = $_.fullName.replace($sourcePath.replace('\*',''), $destPath)
if (!(Test-Path -path (split-path $sp)))
{
New-Item (split-path $sp) -Type Directory
}
copy-item $_.fullname $sp -force
}
As long as the files are in one directory, the following should work fine. It might be a bit more verbose than needed, but it should be a good starting point.
$sourcePath = "c:\sourcePath"
$destPath = "c:\destPath"
$items = Get-ChildItem $sourcePath | Where-Object {($_.FullName -like "*.exe") -or ($_.FullName -like "*.exe.config") -or ($_.FullName -like "*.dll")}
$items | % {
Copy-Item $_.Fullname ($_.FullName.Replace($sourcePath,$destPath))
}