PowerShell script isn't copying like I want - powershell

Right in the beginning I should note that I am a bloody beginner because I can't attend it classes in my grade.
I want to create a PowerShell script which will copy everything from
C:\Users\Robert\Desktop\test(lots of folders)
to
C:\Users\Robert\Desktop\neu(lots of folders with the exact same names as above)\price
As an absolute beginner I thought that it will be ok to replace the variable folder name with $_Name because it is the same name in both but I am obviously wrong and don't know why.
Here is my attempt
Copy-Item "C:\Users\Robert\Desktop\test\$_name\*" -Destination "C:\Users\Robert\Desktop\neu\$_Name\price" -Recurse
It is copying something but in one package in a new folder in "neu".
I can't avoid creating this script because it would take me at least two or three days to do it by hand.
I am also sorry for my poor English skills
Thank you

the $_ represents the current pipeline item. i don't see a pipeline in there ... [grin]
the following works by grabbing every file in the source dir & its subdirs, and copying that structure to the destination dir. it uses Splatting to structure the parameters neatly.
$SourceDir = "$env:TEMP\Apps - Copy"
$DestDir = "$env:TEMP\Apps - Copy - Two"
$CI_Params = #{
LiteralPath = $SourceDir
Destination = $DestDir
Force = $True
Recurse = $True
}
Copy-Item #CI_Params

If my understanding is correct:
$src = 'C:\Users\Robert\Desktop\test'
$dst = 'C:\Users\Robert\Desktop\neu\{0}\price'
Get-ChildItem $src -Directory | ForEach-Object {
Copy-Item -Path "$($_.FullName)\*" -Destination ($dst -f $_.BaseName) -Recurse -Force -WhatIf
}
Remove -WhatIf to actually do it.

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

PowerShell script does not copy subfolders and their content

It's my first time using a custom Powershell script and I think I might be missing something. Everything works, except it doesn't copy subfolders and their content.
$sourcePath = 'C:\Users\User\Desktop\test\test1'
$destinationPath = 'C:\Users\User\Desktop\test\test2'
$files = Get-ChildItem -Path $sourcePath -Recurse
$filecount = $files.count
$i=0
Foreach ($file in $files) {
$i++
Write-Progress -activity "Moving files..." -status "($i of $filecount) $file" -percentcomplete (($i/$filecount)*100)
# Determine the absolute path of this object's parent container. This is stored as a different attribute on file and folder objects so we use an if block to cater for both
if ($file.psiscontainer) {$sourcefilecontainer = $file.parent} else {$sourcefilecontainer = $file.directory}
# Calculate the path of the parent folder relative to the source folder
$relativepath = $sourcefilecontainer.fullname.SubString($sourcepath.length)
# Copy the object to the appropriate folder within the destination folder
copy-Item $file.fullname ($destinationPath + $relativepath)
}
i'm sure it's something to do with Path instead of Root or the Recurse option that's not used corretly.
If someone could help me it would be really appreciated.
Thank you!
To copy recursively, use
Copy-Item -Path <src> -Destination <dest> -Recurse
See Official documentation.
This may very well work for you.
As for walking through a previously built list of source files and copying one-by-one as you did, it should work. I did not check/debug the code you put together.
If you actually need this walk through, simply debug your code by printing $files and the source and target of your copy-Item, this will tell you the source of problems. Please post this output in the OP.
You may have to use the option -Directory for Get-ChildItem, or class [System.IO.Path].

PowerShell to copy files to destination's subfolders while excluding certain folders in the destination

So I have danced with this off and on throughout the day and the timeless phrase "There's more than one way to skin a cat" keeps coming to mind so I decided to take to the community.
Scenario:
Source folder "C:\Updates" has 100 files of various extensions. All need to be copied to the sub-folders only of "C:\Prod\" overwriting any duplicates that it may find.
The Caveats:
The sub-folder names (destinations) in "C:\Prod" are quite dynamic and change frequently.
A naming convention is used to determine which sub-folders in the destination need to be excluded when the source files are being copied (to retain the original versions). For ease of explanation lets say any folder names starting with "!stop" should be excluded from the copy process. (!stop* if wildcards considered)
So, here I am wanting the input of those greater than I to tackle this in PS if I'm lucky. I've tinkered with Copy-Item and xcopy today so I'm excited to hear other's input.
Thanks!
-Chris
Give this a shot:
Get-ChildItem -Path C:\Prod -Exclude !stop* -Directory `
| ForEach-Object { Copy-Item -Path C:\Updates\* -Destination $_ -Force }
This grabs each folder (the -Directory switch ensures we only grab folders) in C:\Prod that does not match the filter and pipes it to the ForEach-Object command where we are running the Copy-Item command to copy the files to the directory.
The -Directory switch is not available in every version of PowerShell; I do not know which version it was introduced in off the top of my head. If you have an older version of PowerShell that does not support -Directory then you can use this script:
Get-ChildItem -Path C:\Prod -Exclude !stop* `
| Where-Object { $_.PSIsContainer } `
| ForEach-Object { Copy-Item -Path C:\Updates\* -Destination $_ -Force }
To select only sub folders which do not begin with "!stop" do this
$Source = "C:\Updates\*"
$Dest = "C:\Prod"
$Stop = "^!stop"
$Destinations = GCI -Path $Dest |?{$_.PSIsContainer -and $_.Name -notmatch $Stop }
ForEach ($Destination in $Destinations) {
Copy-Item -Path $Source -Destination $Destination.FullName -Force
}
Edited Now copies all files from Update to subs of Source not beginning with "!stop" The -whatif switch shows what would happen, to arm the script remove the -whatif.
Edit2 Streamlined the script. If also Sub/sub-folders of C:\Prod shall receive copies include a -rec option to the gci just in front of he pipe.

Define folder depth for the verbose option in Copy-Item cmdlet

I'm using the following command to copy a directory tree from one folder to another.
Copy-Item $SOURCE $DEST -Filter {PSIsContainer} -Recurse -Force -Verbose
The verbose option is correctly showing each folder that is copied. However, I would like to tell the Verbose option to only shows the first level of the subfolders that are copied. Hence the subfolders/subfolders/... etc wouldn't appear.
Is it possible?
Instead of using the -Verbose option, you could use the -PassThru option to process the successfully processed items via the pipeline. In the following example, I am assuming that $DEST is the existing directory in which the newly copied directory will appear. (You cannot call Get-Item on non-existant objects.)
$SOURCE = Get-Item "foo"
$DEST = Get-Item "bar"
Copy-Item $SOURCE $DEST -Filter {PSIsContainer} -Recurse -Force -PassThru | Where-Object {
# Get the parent object. The required member is different between
# files and directories, which makes this a bit more complex than it
# might have been.
if ($_.GetType().Name -eq "DirectoryInfo") {
$directory = $_.Parent
} else {
$directory = $_.Directory
}
# Select objects as required, in this case only allow through
# objects where the second level parent is the pre-existing target
# directory.
$directory.Parent.FullName -eq $DEST.FullName
}
Count the number of backslashes in the path and add logic to select first level only perhaps. Something like this perhaps?
$Dirs=get-childitem $Source -Recurse | ?{$_.PSIsContainer}
Foreach ($Dir in $Dirs){
$Level=([regex]::Match($Dir.FullName,"'b")).count
if ($Level -eq 1){Copy-Item $Dir $DEST -Force -Verbose}
else{Copy-Item $Dir $DEST -Force}}
*Edited to include looping and logic per requirements
I would suggest using robocopy instead of copy-item. Its /LEV:n switch sounds like it's exactly what you're looking for. Example (you'll need to test & tweak to meet your requirements):
robocopy $source $dest /LEV:2
robocopy has approximately 7 gazillion options you can specify to get some very useful and interesting behavior out of it.

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