Search sub-directories using robocopy - powershell

I wrote a quick robocopy script to search a directory and move video files that match certain criteria (size, name, extension). It works great except it will not search sub-directories. I tried the /s flag but that moves over the entire directory. I only want the files in those directories. Is there an easy way to plug the robocopy portion into a Get-Child or a loop in powershell? Thanks!
robocopy C:\psTesting\sourceDrive\ C:\psTesting\movieFolder\ *.mp4 *.avi *.mkv /min:600000000 /mov /np

Easiest way that I can think is to make a function for it. This should do what you want:
Function PSRoboCopy{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$true,ValueFromPipelinebyPropertyName=$True)]
[Alias('SourceFile')]
[String[]]$FullName,
[string]$Destination,
[string]$Options
)
Process{
$FileIn = Split-Path $FullName -Leaf
$FolderIn = Split-Path $FullName
& robocopy $FolderIn $Destination $FileIn $Options
}
}
Get-ChildItem 'C:\psTesting\sourceDrive\*' -Include '*.mp4','*.avi','*.mkv' -Recurse | Where{$_.length -gt 600000000} | PSRoboCopy -Destination 'C:\psTesting\movieFolder\' -Options "/mov /np"
That lets you pipe a file object directly into it and specify your destination and any options you want.

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

Moving or copying several files at once in Powershell

In bash, you can
cp filea fileb filec Folder
which will copy filea fileb and filec into Folder
For some reason, i couldn't find a way to do the same thing in Powershell.
Everywhere i looked, all i could find is just the possibility to use wildcards in order to move
a bunch of files from the same type like:
cp *.txt Folder
But that's not what i'm looking for. I'm looking for a way to copy several files
by their names in one command.
Does anybody knows a way to do that?
"I'm looking for a way to copy several files
by their names in one command."
Here are three examples that can do what you want. The first one is closest to what you're looking for.
param(
$files = #('filea', 'fileb', 'filec'),
$dest = 'Folder'
)
Copy-Item -LiteralPath $files -Destination $dest -Verbose
$files | ForEach-Object { Copy-Item -Path $_ -Destination $dest -Verbose }
foreach ($file in $files) { Copy-Item -Path $file -Destination $dest -Verbose }
A one liner to do:
#("file1","file2","filen") | % {Copy -Path $_ -Destination "destination"}

Copy folder structure and content of specific folders

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

Copy files in a folder structure to their respective sub-folder with Powershell

I want to move the file "file_to_move.txt"in each folder to their respective "done"-folder.
so the file_to_move.txt in C:\Temp\test\folder1 is moved to C:\Temp\test\folder1\done
and file_to_move.txt in C:\Temp\test\folder2 is moved to C:\Temp\test\folder2\done
...and so on, preferably with a %date%_%time% added to the file-name.
if a folder (like folder4 in the example below) does not have a file_to_move.txt, the script should just ignore it and move on.
folder structure example:
C:\Temp\test\DONE
C:\Temp\test\folder1
C:\Temp\test\folder1\done
C:\Temp\test\folder1\some_other_folder
C:\Temp\test\folder1\some_other_file.txt
C:\Temp\test\folder1\file_to_move.txt
C:\Temp\test\folder2
C:\Temp\test\folder2\done
C:\Temp\test\folder2\some_other_folder
C:\Temp\test\folder2\some_other_file.txt
C:\Temp\test\folder2\file_to_move.txt
C:\Temp\test\folder3
C:\Temp\test\folder3\done
C:\Temp\test\folder3\some_other_folder
C:\Temp\test\folder3\some_other_file.txt
C:\Temp\test\folder3\file_to_move.txt
C:\Temp\test\folder4
C:\Temp\test\folder4\done
C:\Temp\test\folder4\some_other_folder
C:\Temp\test\folder4\some_other_file.txt
I have experimented with a Powershell script even if I'm not very good at it and I dont know it can be done in a standard batch-script.
I have tried this so far:
In a batch-script:
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%bin\movescript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
in the movescript.ps1:
Move-Item C:\Temp\test\*\file_to_move.txt C:\Temp\test\*\done\file_to_move_$(get-date -f yyyyMMdd_HHmmss).txt
But this is not working.
I guess it's not precise enough to work.
As a bonus, can the whole thing be done within the basic script or must we use the external .PS1-file?
You can use the Get-ChildItem cmdlet with a filter to retrieve all file_to_move.txt files recursively from a path. Use the Foreach-Object (alias foreach) to iterate over them and combine the new path using the Join-Path cmdlet. To Copy the Item, you can use the Copy-Item cmdlet:
$itemsToCopy = Get-ChildItem -Path c:\Temp\Test -Filter file_to_move.txt -Recurse
$itemsToCopy | foreach {
$newPath = Join-Path $_.DirectoryName 'done'
New-Item -Path $newPath -ItemType directory -Force | out-null
$_ | Copy-Item -Destination $newPath
}
If you want to add a Timestamp, you could use the Get-Date cmdlet and invoke the ToString method with your desired format on it, example:
(Get-Date).ToString("yyyy-dd-M_HH-mm-ss")
Output:
2016-05-4_15-06-02
You can now concat the filenames using a format string and the $_.Basename and $_.Extension property within your foreach loop. I will leave this as an exercise to you.

Powershell restore previous version of the files

We got hit by virus, it changed all common file extension to .kmybamf (.txt >> .txt.kmybamf ) and if I delete .kmybamf , the file got damaged.....
So I made a list of all files that got damaged. now I'm trying to overwrite them by previous version. Anyone knows how to do it in Powershell?
I can do it in cmd similar to this
subst X: \localhost\D$\#GMT-2011.09.20-06.00.04_Data
robocopy Z: D:\Folder\ /E /COPYALL
But I want to do it in one shot in Powershell, It has to be a "if .kmybamf found, then restore previous version." and powershell seems like has no such cmdlet for restoring previous version of files or folders.
$fileList = Get-Content -Path "\\localhost\D$\#GMT-2011.09.20-06.00.04_Data"
$destinationFolder = "D:\Folder\"
foreach ($file in $fileList)
{
Copy-Item -Path $file -Destination $destinationFolder -Force
}
This will also work but I find it less readable
Get-Content -Path "\\localhost\D$\#GMT-2011.09.20-06.00.04_Data" | ForEach-Object { Copy-Item -Path $_ -Destination "D:\Folder" -Force }
Get-Content is for reading the text from the files, to read the files from a folder you would have to use Get-Childitem