Need to copy single file to all subfolders recursively - powershell

Please can someone help me create a powershell or CMD script, as simple as possible (1 line?) that will do the following...
-Take file (c:\test.txt)
-Copy to ALL subfolders within a given folder, including multiple levels deep
eg, c:\test1\1\2\3\ and c:\test2\6\7\8\
-Without overwriting that file if it already exists
-Not changing ANY existing files. Just adding the original txt file if it doesn't exist.
I've tried a bunch of scripts I found online, changing them, but have been unsuccessful. Either it overwrites existing files, or doesn't go multiple levels deep, or skips all the folders between top/bottom levels, or throws errors. I give up.
Thanks
Matt

How about something like this...
$folders = Get-ChildItem -Recurse -Path C:\temp\1 -Directory
$file = "c:\temp\test.txt"
foreach($folder in $folders){
$checkFile = $folder.FullName + "\test.txt"
$testForFile=Test-Path -Path $checkFile
if(!$testForFile){
Copy-Item $file -Destination $folder.FullName
}
}

Not a one-liner, but here you go:
$rootFolder = 'PATH OF FOLDER CONTAINING ALL THE SUBFOLDERS'
$fileToCopy = 'c:\test.txt'
$fileName = [System.IO.Path]::GetFileName($fileToCopy)
Get-ChildItem -Path $rootFolder -Recurse -Directory | ForEach-Object {
if (!(Test-Path -Path (Join-Path -Path $_.FullName -ChildPath $fileName) -PathType Leaf)) {
Copy-Item -Path $fileToCopy -Destination $_.FullName
}
}

Related

How to move items excluding certain folder using PowerShell?

The question is an oversimplification of the real issue.
I have a folder let's call it "ParentFolder". Within this folder are files and subfolders. I want all the files and subfolders moved from the "ParentFolder" except one specific subfolder, let's call it "SpecificChildFolder". For the "SpecificChildFolder" I don't want the folder to be moved but only the files in it.
I can do these two tasks separately. I can either move all the files and folders(including the "SpecificChildFolder) in the "ParentFolder" or I can move files from the "SpecificChildFolder" only (excluding the rest of the files and subfolders in the "ParentFolder").
I want these two tasks to happen simultaneously.
I thought I would accomplish this in two separate functions:
Move everything except "SpecificChildFolder"
Move files from within the "SpecificChildFolder"
The stage# 2 code works. It is Stage# 1 I have issues with.
I have also tried Get-ChildItem $src -ErrorAction SilentlyContinue | Where-Object {$_.Directory.Name -NotLike "*SpecificChildFolder*"} | ForEach-Object{} but this doesn't work either
Secondly, can this is not happen in one line of PowerShell?
I am using PowerShell Core 7.2
Code for Stage 1:
#Sources
$src = "C:\User\Desktop\TEST\ParentFolder\*"
$srcMcaNameChg = "C:\User\Desktop\TEST\ParentFolder"
#Destination
$dest = "C:\Users\harguls\Desktop\TEST\DestinationFolder"
Function MoveFiles{
Param(
[string]$src,
[string]$dest,
[string]$srcNameChange
)
Get-ChildItem $src -Recurse -Exclude 'SpecificChildFolder' -ErrorAction SilentlyContinue | ForEach-Object{
$fileName = $_.Name
# Check for duplicate files
$file = Test-Path -Path $dest\$fileName
Write-Output $file
if($file)
{
"$srcNameChange\$fileName" | Rename-Item -NewName ("Copy_"+$fileName)
}
}
Move-Item -Path $src -Destination $dest -Force
}
MoveFiles -src $src -dest $dest -srcNameChange $srcMcaNameChg
Here is a vague representation of what it seems you're looking to accomplish, hope the inline comments are explanatory.
$source = '\path\to\source\folder'
$destination = '\path\to\destination\folder'
# you can add multiple folders here if you want
$exceptWith = 'foldertoexclude1', 'foldertoexclude2'
# get only the subfolders of `$source` and iterate over them
Get-ChildItem $source -Directory | ForEach-Object {
# if this folder name is in the folders to exclude array
if($_.Name -in $exceptWith) {
# get only the files of this folder and move them to destination
Get-ChildItem $exclude -File | Move-Item -Destination $destination
# if we're here we can proceed with next subfolder
return
}
# if we're here means that the name of the folder was not in `$exceptWith`
# so we move this folder and all it's child folders / files
Move-Item -LiteralPath $_.FullName -Destination $destination -Recurse
}

Moving contents of a folder up one level based on folder name

I have a directory of information that is separated into document numbers so each folder that contains documents starts with DOC-######-NameOfDocument. The thing I am trying to do is create a PowerShell script that will search a directory for any folders with a specified document number and then take the contents of that folder, move it up one level, and then delete the original folder (which should now be empty).
Below is the closest I have gotten to my intended result.
$Path = "filepath"
$Folders = Get-ChildItem -Filter "DOC-#####*" -Recurse -Name -Path $Path
$companyID = "######"
foreach ($Folder in $Folders){
$filepath = $Path + $Folder
$Files = Get-ChildItem -Path $filepath
$imagesourc = $filepath + $companyID
$imageDest = $filepath.Substring(0, $filepath.LastIndexOf('\'))
if (Test-Path -Path $imagesourc){
Copy-Item -Path $imagesourc -Destination $imageDest -Recurse
}
foreach ($File in $Files){
$Parent_Directory = Split-Path -Path $File.FullName
$Destination_Path = $filepath.Substring(0, $filepath.LastIndexOf('\'))
Copy-Item -Path $File.FullName -Destination $Destination_Path -Recurse
if ($null -eq (Get-ChildItem -Path $Parent_Directory)) {
}
}
Remove-Item $filepath -Recurse
}
This does what I need but for whatever reason I can't Devine, it will not work on .HTM files. Most of the files I am moving are .html and .htm files so I need to get it to work with .htm as well. The files with .HTM will not move and the folder won't be deleted either which is good at least.
Try using this:
$ErrorActionPreference = 'Stop'
$fileNumber = '1234'
$initialFolder = 'X:\path\to\folders'
$folders = Get-ChildItem -Path $initialFolder -Filter DOC-$fileNumber* -Force -Directory -Recurse
foreach($folder in $folders)
{
try
{
Move-Item $folder\* -Destination $folder.Parent.FullName
Remove-Item $folder
}
catch [System.IO.IOException]
{
#(
"$_".Trim()
"File FullName: {0}" -f $_.TargetObject
"Destination Folder: {0}" -f $folder.Parent.FullName
) | Out-String | Write-Warning
}
catch
{
Write-Warning $_
}
}
Important Notes:
Move-Item $folder\* will move all folder contents recursively. If there are folders inside $folder, those will also be moved too, if you want to target folders which only have files inside, an if condition should be added before this cmdlet.
Try {...} Catch {...} is there to handle file collision mainly, if a file with a same name already exists in the parent folder, it will let you know and it will not be moved nor will the folder be deleted.
-Filter DOC-$fileNumber* will capture all the folders named with the numbers in $fileNumber however, be careful because it may capture folders which you may not intent to remove.
Example: If you want to get all folders containing the number 1234 (DOC-12345-NameOfDocument, DOC-12346-NameOfDocument, ...) but you don't want to capture DOC-12347-NameOfDocument then you should fine tune the filter. Or you could add the -Exclude parameter.
-Force & -Directory to get hidden folders and to target only folders.

ForEach-Object to look in subfolders

I Have this powershell script “copFiles.ps1” that looks in a txt file "Filestocopy.txt" for a list and copies them to a destination
$source = "C:\Data\Filestocopy.txt"
$destination = "C:\Data\Models"
Get-Content $source | ForEach-Object {copy-item $_ $destination}
It’ll only copy the files if they’re in the same folder as the .ps1 file and it ignores subfolders, how can I get it to look in subfolders of the folder that its in, I gather I need to use the -recurse option but don’t know how to rewrite it so it works.
The .ps1 file is fired by a bat file.
Many thanks
I don't know how fast this will be, but you can give an array as the argument for the -Path parameter of Get-ChildItem add the -Recurse switch to dig out the files in subdirectories and simply pipe them along to Copy-Item. something like:
Get-ChildItem (Get-Content $Source) -Recurse |
Copy-Item -Destination $destination
You may also want to add the -File switch.
Update
Based on your comment I played around with this a a little more:
$source = "C:\Data\Filestocopy.txt"
$Destination = "C:\data\Models"
# Get-ChildItem (Get-Content $Source) -Recurse |
Get-ChildItem (Get-Content $Source) -Recurse -File |
ForEach-Object{
If( $_.Directory.FullName -eq $Destination )
{ # Don't work on files already present in the destination
# when the destination is under the current directory...
Continue
}
$FileNum = $null
$NewName = Join-Path -Path $Destination -ChildPath $_.Name
While( (Test-Path $NewName) )
{
++$FileNum
$NewName = Join-Path -Path $Destination -ChildPath ($_.BaseName + "_" + $FileNum + $_.Extension)
}
Copy-Item $_.FullName -Destination $NewName
}
This will increment the destination file name in cases where a file by that name already exists in the destination. If the destination is under the current directory it will prevent analyzing those files by comparing the path of the file to the destination. Files must have unique names in a given folder so I'm not sure how else it can be handled.

Move-Item moves entire source directory instead of specified files

In PowerShell, I'm trying to use Move-Item to move all files containing the string "2017", located in $Source, to $MovieDst
The files that I want to move in $Source are individual .mkv files with the string "2017" in the filename, located at the root of $Source - they are not in a directory.
If I hash out Move-Item and un-hash Write-Host $NoDirMovie, it outputs the following:
ExampleFile.2017.mkv
ExampleFile2.2017.mkv
So, it definitely knows which are the correct files to be referencing. But as soon as I use Move-Item, it moves the entire $source directory into $MovieDst. I can't use -Exclude *COMPLETED_DOWNLOADS* as it includes the very files that I want to move.
So, I'm a bit stuck - It can't move my files, because it FIRST wants to move the entire directory containing them. Is there a way to stop it from moving the entire $Source directory, and JUST move only the contents of $Source that contain the string "2017" ?
$Source = "C:\TV Shows 7\COMPLETED_DOWNLOADS"
$MovieDst = "C:\TV Shows 7\COMPLETED DOWNLOADS_Sorted\MOVIES"
$NoDirMovies = dir $Source *2017*
foreach($NoDirMovie in $NoDirMovies | where {$_ -ne $Source})
{
Move-Item -Path $Source -Destination $MovieDst
#Write-Host $NoDirMovie
}
Why are you storing the files you want in $NoDirMovie, then passing $Source to Move-item?
Surely you want:
foreach($NoDirMovie in $NoDirMovies | where {$_ -ne $Source})
{
Move-Item -Path $NoDirMovie -Destination $MovieDst
#Write-Host $NoDirMovie
}
Edit
That's a simple fix, which I haven't tried. A few things:
dir is an alias for the built-in PowerShell Cmdlet Get-ChildItem
Your loop Where is illogical. $NoDirMovies are files in directory $Source, so they will never equal $Source (assume this was an attempt to not copy directory?)
Worth checking if the directory you are trying to copy to exists.
V2 (I tried this one)
$Source = "C:\TV Shows 7\COMPLETED_DOWNLOADS"
$MovieDst = "C:\TV Shows 7\COMPLETED DOWNLOADS_Sorted\MOVIES"
$filter = "2017"
If(!(Test-Path $MovieDst)){
New-Item -ItemType Directory -Path $MovieDst
}
foreach($file in (Get-ChildItem $Source | Where-object {$_.Name -match $filter})){
Move-Item -Path $file.FullName -Destination $MovieDst
}
IMHO your approach is overcomplicated. Move-Item accepts piped input, so
$Source = "C:\TV Shows 7\COMPLETED_DOWNLOADS"
$MovieDst = "C:\TV Shows 7\COMPLETED DOWNLOADS_Sorted\MOVIES\"
Get-ChildItem -Path $Source -Filter "*2017*.mkv" |Where {!($_.PSIsContainer)}
Move-Item -Destination $MovieDst -WhatIf
If the outpout looks OK, remove the -WhatIf

Recursively copying files from a source to a single directory including searching ZIP files

I'd like to use Powershell to recursively copy a specific set of files from a series of sub directories into a single folder (without the source folder structure). The files all follow the filename format "Writing (*).xlsm". Also, some of the directories have their files stored in a ZIP file and I'd like to search these ZIP files for this same file name.
I've seen plenty of solutions that can copy the files but the result recreates the folder structure that the files were found in but I need to capture them all into a single directory, and I have not seen anything that seems to include the ability to add the opening of ZIP files to the search.
Try this:
$Path = "C:\"
$Destination = "C:\Destination"
$Files = #()
$Files = #(Get-ChildItem -Path "$Path" -Filter "Writing*.xml" -Recurse).Fullname
#Zip
$ZipFiles = #(Get-ChildItem -Path "$Path" -Filter "*.Zip" -Recurse).Fullname
[Void][Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
Add-Type -AssemblyName System.IO.Compression.FileSystem
$ToRemove = #()
Foreach($Zip in $ZipFiles){
$RawFiles = [IO.Compression.ZipFile]::OpenRead($Zip).Entries
foreach($RawFile in $RawFiles) {
If($RawFile.FullName -like "Writing*.xml"){
$outpath = $Zip.Remove($Zip.Length - 4)
[System.IO.Compression.ZipFile]::ExtractToDirectory($Zip, $outpath)
$Files += "$outpath\" + $RawFiles.Fullname
$ToRemove += $outpath
}
}
}
Foreach($File in $Files){
Copy-Item -Path $Files -Destination $Destination
}
Foreach($Folder in $ToRemove){
Remove-Item -Path $Folder -Force
}