Using Robocopy in Powershell - powershell

I am having a challenge while trying to move a large number of files from one location to another. I have a list of folders that I want to move in a .csv and I want to move those items to a new network location. When I use robocopy, only the content of the folders is moved, not the top level folder. This leads to unorganized files. I have tried using move-item but there is not a good logging feature that I can get to work. So here I am.
I am trying to use robocopy but I need to create a destination folder based on the last part of the source path for each item in a list. I have been working on this for hours, please help.
GC -Path C:\Test_1\Test_List.csv |
ForEach-Object
{
$Destination = new-item NAME BASED ON PART OF FILE -itemtype directory |
Robocopy $_ $Destination /e /move /LOG+:C:\Test_2\Test_Copy_Log.txt /NP
}

Related

Powershell Cannot create a file when that file already exists when using Move-Item

I'm trying to move png images in subfolders to a subfolder in the folder they are in.
Main folder is called "stuff", images are in variously named subfolders in the "stuff" main folder, and each of these subfolders have a folder name "Oneshot" in them, I'm trying to move these images that are in M:/Stuff/FolderExample/ to M:/Stuff/FolderExample/Oneshot/.
Current code I am using
Get-ChildItem -Path C:\Users\a\Desktop\stuff\*.png -Recurse | Move-Item -Destination "$($_.FullName)\Oneshot\"
How can I make this work? I'm just trying to learn powershell so I can automate this process as I otherwise would need to repeat the process manually for thousands of times
Use a scriptblock ({...}) as the argument to -Destination - this will allow you to access the current pipeline item as $_ during parameter binding:
Get-ChildItem -Path C:\Users\a\Desktop\stuff\*.png -Recurse |Where-Object Directory -notlike *\OneShot | Move-Item -Destination {"$($_.Directory.FullName)\Oneshot\"}
The Where-Object command in the middle of the pipeline will ensure we don't attempt to move pictures already in one of the OneShot folders.
Powershell is not the best idea to move files. The main reason is that you run into problems in case the path becomes too long >256 characters. I would use robocopy for such tasks. It is part of every current windows version.
robocopy M:/Stuff/FolderExample/ M:/Stuff/FolderExample/Oneshot/ /MIR
Please keep in mind that this code will also delete everything that is in M:/Stuff/FolderExample/Oneshot/ and not in M:/Stuff/FolderExample/. So use /MIR with caution.
You could still invoke robocopy in an ps1 file and wrap other powershell code around it e.g. for variables.

Moving files and folder structure based on days from one drive to another using powershell

I have requirement as below.
Source : C:\s
Destination: C:\d
files located are more than 255 characters.
Moving files based on last modified or written days(10) and it should copy complete folder structure, if any of the files not modified as per the last modified date it should be available at the source file in the same folder. While the other is modified, it should be created with a new directory with the same folder structure and file to moved in the same location as per the source location path.
I have tried PowerShell script using days, however the files are being copied into and folders are staying at the source itself.
Get-ChildItem -Path C:\s -Recurse | Where-Object {$_.LastWriteTime -lt (Get-date).AddDays(0)} | Move-Item -destination C:\d
So far the output is giving only files but not the folder structure, if it is empty folder it should be moved to the destination folder.
Thanks
Suman
The issue is that you are getting all the files with Get-ChildItem -Recurse and filtering them, but when those files are piped to Move-Item -Destination you essentially are saying to take all files (regardless of the source) and put them into the single folder C:\d. If you want to preserve the directory structure, you have to pass through and specify the directory structure in the Move-Item -Destination parameter.
#AdminOfThings is correct, an easier way to do the move is to use Robocopy. Experimenting with the various switches should get you what you need. e.g.:
Robocopy.exe C:\s C:\d /move /e /minage:10
Where:
/move says to move instead of copy
/e is copy directories including empty ones
/minage:10 is to move files older than 10 days.

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

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

Copy-Item vs XCopy

I was asked to benchmark using a powershell script to do some basic file copying as opposed to using XCopy in a batch file. It seems to run in roughly about the same amount of time, but with Powershell the parent folder of the tree structure is not being created in the destination, whereas with XCopy the parent folder does get created. For example, with
xcopy D:\Webs\First\*.* D:\Test\Sandbox\ /E
The "First" folder does get created with all its contents. Whereas with
$SourceFolder = "D:\\Webs\\First\\"
$TargetFolder = "D:\\Test\\Sandbox\\"
Copy-Item $SourceFolder $TargetFolder -recurse
The folder named "First" does not get created and rather the contents are copied to the destination. If I use "Webs" as the source folder, there are other folders at the same level as "First" that get copied as well and that is not desirable.
How do I get the Parent folder "First" to copy to the destination using the Copy-Item or some other powershell command without manually creating this folder within the script so I get the exact same results?
Thanks...
for a quick way you can append the source parent folder before copy, e.g.
$TargetFolder = "D:\\Test\\Sandbox\\"+($SourceFolder | Split-Path -leaf)
you can also use the Measure-Command Cmdlet for timings