Copy-Item / Remove-Item child-content only without root folder? - powershell

Hi I'm struggling mightily with the following - suppose I have the following directory structure C:\Temp\Test1 and C:\Temp\Test2
What I'd like to do is recursively copy the child-contents of C:\Temp\Test1 to C:\Temp\Test2 without copying the actual folder C:\Temp\Test1 ..right now if I use the command
Copy-Item C:\Temp\Test1 C:\Temp\Test2 -Recurse
Will result in C:\Temp\Test2\Test1 and no combination of parameters seems to alleviate the problem
Similarly, when I wish to remove all the child content in C:\Temp\Test2 I wish to only delete the child content and not the actual folder eg
Remove-Item C:\Temp\Test2\ -Recurse
Is removing the \Test2 folder. I've tried so many variations of parameters - how can I accomplish what I am trying to do?

Take a look at the get-childitem command. You can use this in the pipeline to copy or remove all items underneath the root folders:
# recursively copy everything under C:\Temp\Test1 to C:\Temp\Test2
get-childitem "C:\Temp\Test1" | % {
copy-item $_.FullName -destination "C:\Temp\Test2\$_" -recurse
}
# recursively remove everything under C:\Temp\Test1
get-childitem "C:\Temp\Test1" -recurse | % {
remove-item $_.FullName -recurse
}

Copy-Item C:\Temp\Test1\* C:\Temp\Test2
Remove-Item "C:\Temp\Test2\*" -recurse
Works too :)

Related

Powershell Flatten dir recursively

Found a couple of semi-related links:
How to merge / 'flatten' a folder structure using PowerShell - recursive
but my ask is that I have a root dir P:/files
which has several layers of sub directories etc I'd like to flatten all of them so that all the -Files are moved just to the root of P:/files
I don't need to be concerned with duplicates as I'll make sure there are non well before this stage.
it looks like I can use powershell to get a list of all the files no matter the level, and then just for-each over them and a move?
Get-ChildItem -LiteralPath P:\files -Directory | Get-ChildItem -Recurse -File
help on the loop?
A single recursive Get-ChildItem that pipes to Move-Item should do:
Get-ChildItem -File -Recurse -LiteralPath P:\files |
Move-Item -Destination $yourDestination -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
If you need to exclude files directly located in P:\files:
Get-ChildItem -Directory -Recurse -LiteralPath P:\files | Get-ChildItem -File |
Move-Item -Destination $yourDestination -WhatIf
Note the use of -Directory and -Recurse first, so that all subdirectories in the entire subtree are returned, followed by a non-recursive Get-ChildItem -File call that gets each subdirectory's immediate files only.

Move Files from one folder to another without using -Recurse

I have a powershell script where all I want to do is move files of a certain type from the folder specified to a destination folder. Unfortunately this doesn't work without -Recurse contained which is not the functionality I want to achieve. If I include -Recurse it starts to move images from "D:/Photos/x/" etc. All i want to do is move files from "D:/Photos" to the destination
Get-ChildItem "D:\Photos" -Include *.png, *.jpeg, *.jpg |
Move-Item -Destination "D:\Photos\Powershell Task" -Force
The -Include parameter without -Recurse does not work.
However, there is another way to have the -Include do what you want (namely filter on more than one extension) and that is to add \* to the Path.
In your case:
Get-ChildItem -Path "D:\Photos\*" -Include *.png, *.jpeg, *.jpg |
Move-Item -Destination "D:\Photos\Powershell Task" -Force
Mind you: The destination path D:\Photos\Powershell Task needs to exist.
Solution :
Get-ChildItem "D:\Photos\*" -Include *.png, *.jpeg, *.jpg | Move-Item -Destination "D:\Photos\Powershell Task" -Force
Explanation :
When selecting Get-ChildItem to use include you must have -recursion selected OR define what the include is represented using wildcards in the -path.
This case D:\Photos\* is defining to get all children in D:\Photos that have both a name and extension then you are filtering those by using -include to say only the ones with extensions .jpg .png

Powershell script to copy files based on filename

I have a folder that contains several thousand files. I would like to write a Powershell script that loops through the files and copies each file whose filename contains a specific keyword. In pseudocode:
For each file in C:\[Directory]
If filename contains "Presentation" Then
copy file in C:\[Directory 2]
Simply like this ?
copy-item "C:\SourceDir\*Presentation*" "C:\DestinationDir"
or like this :
copy-item "C:\SourceDir\*" "C:\DestinationDir" -Filter "*rrrr*"
But a risk exist if you have a directory with "presentation" in his name into the source directory. Then take all method proposed here and add -file in get-childitem command.
Like in this short version of Robdy code :
gci "C:\SourceDir" -file | ? Name -like "*Presentation*" | cpi -d "C:\DestinationDir"
That code should do the trick:
$files = Get-ChildItem -Path "C:\path\to\source\folder"
$files | Where-Object Name -Like "*Presentation*" | Copy-Item -Destination "C:\path\to\destination\folder"
Of course can be written in one line but I put in two for visibility.
Edit: as Esperento57 pointed out, you might want to add -ItemType File to Get-ChildItem cmdlet to not include folders with 'Presentation' in their name. Also, depending on your needs you might also want to use -Recurse param to include files in subfolders.
If you have files in subfolders and you want to keep the path in destination folder you'll have to change the script a bit to something like:
Copy-Item -Destination $_.FullName.Replace('C:\path\to\source\folder','C:\path\to\destination\folder')
And for the above you'll have to make sure that folders are actually created (e.g. by using -Force for Copy-Item.
This seems to work:
$src = "Dir1"
$dst = "Dir2"
Get-ChildItem $src -Filter "*Presentation*" -Recurse | % {
New-Item -Path $_.FullName.Replace($src,$dst) -ItemType File -Force
Copy-Item -Path $_.FullName -Destination $_.FullName.Replace($src,$dst) -Force
}
Try something like this:
Get-ChildItem "C:\Your\Directory" -File -Filter *YourKeyWordToIsolate* |
Foreach-Object { Copy-Item $_.FullName -Destination "C:\Your\New\Directory" }
... but, of course, you'll need to fill in some of the blanks left open by your pseudocode example.
Also, that's a one-liner, but I inserted a return carriage for easier readability.

How to use the Copy-Item cmdlet correctly to copy piped files

I am building a small script which should copy all .zip files to a special folder called F:\tempzip.
I tried it with Copy-Item cmdlet, but I didn't manage to do it. The script should copy all files from this folder (recursively) which are ".zip".
This is the part of the script I am talking about:
get-childitem F:\Work\xxx\xxx\xxx -recurse `
| where {$_.extension -eq ".zip"} `
| copy-item F:\tempzip
What do I have to add?
It's a lot simpler than that. Copy-Item has its own -Recurse switch. All you have to do is:
Copy-Item F:\Work\xxx\xxx\xxx\*.zip F:\tempzip -Recurse
When piping items to copy-item you need to tell it that "F:\tempzip" is the destination path.
| Copy-Item -Destination F:\tempzip
You can also cutout piping to the where operator by using Get-ChildItem's parameter -filter.
Get-Childitem "C:\imscript" -recurse -filter "*.zip" | Copy-Item -Destination "F:\tempzip"
Edit: Removal of unnecessary foreach loop and updated explanation.
For whatever reason, the Copy-Item recursion didn't accomplish what I wanted, as mentioned here, and how it is documented to work. If you have a bunch of *.zip or *.jpg files in arbitrarily deep subfolder hierarchies, and you want to copy them to a single place (one flat folder, elsewhere), I had better luck with a piped command involving Get-ChildItem. Say you are currently in the folder containing the root of your search:
Get-ChildItem -Recurse -Include *.zip | Copy-Item -Destination C:\Someplace\Else
That command will copy all the files and not duplicate the folder hierarchies.

XCOPY deployment script - how to include certain files?

I need to copy only certain parts of a folder using Powershell, specifically this list:
$files = #("MyProgram.exe",
"MyProgram.exe.config",
"MyProgram.pdb",
".\XmlConfig\*.xml")
In human readable form: 3 specific MyProgram.* files under root of target folder and all XML files under XmlConfig folder which itself is under root of source path (..\bin\Release\ in my case). XmlConfig folder must be created in destination, if it does not exist.
What I have tried:
(1) I tried the following, but it did not work, i.e. no folder or files were created at the destination path:
Copy-Item -Recurse -Path "..\bin\Release\" -Destination ".\Test\" -Include $files
(2) When -Include is removed, whole folder structure is successfully created, including subfolders and files:
Copy-Item -Recurse -Path "..\bin\Release\" -Destination ".\Test\"
It must be something wrong with my understanding of how -Include filter works:
(3) I tested an assumption that -Include needs an array of wildcards, but this did not work either:
$files = #("*MyProgram.exe*",
"*MyProgram.exe.config*",
"*MyProgram.pdb*",
"*.\XmlConfig\*.xml*")
Please advise on how to properly do Copy-Item in my case.
UPDATE (based on below answers):
I am looking for a generic implementation that takes an array of strings. It opens the possibility to put all necessary files/paths in one place, for easy editing, so that a non-Powershell knowledgeable person can understand and modify it as required. So in the end it would be single script to perform XCOPY deployments for any project, with input file being the only variable part. For above example, the input would look like this (saved as input.txt and passed as an argument to the main script):
MyProgram.exe
MyProgram.exe.config
MyProgram.pdb
.\XmlConfig\*.xml
I would prefer wildcards approach, since not many people know regex.
i don't know what is wrong with filter but you can still do
$files | % { copy-item ..\bin\release\$_ -Destination .\test}
if you want to preserve directoty structure you'll have to weak this a little, like :
$sourcedir="c:\temp\test"
$f=#("existing.txt","hf.csv";"..\dir2\*.txt")
$f |%{
$source=ls (join-Path $sourcedir $_) |select -expand directoryname
if ("$source" -like "$sourcedir*"){
$destination=$source.Substring($sourcedir.Length)+".\"
}
else{
$destination=$_
}
copy-item $sourcedir\$_ -Destination $destination -WhatIf
}
AFAICT -Include works only with file names or directory names and not combinations i.e. paths. You can try something like this:
$files = 'MyProgram\.exe|MyProgram\.exe\.config|MyProgram\.pdb|XmlConfig\\.*?\.xml'
Get-ChildItem ..\bin\release -r | Where {!$_.PSIsContainer -and ($_.FullName -match $files)} |
Copy-Item -Dest .\test
With wildcards you could do it this way:
$files = #('*MyProgram.exe','*MyProgram.exe.config','*MyProgram.pdb','*\XmkConfig\*.xml')
Get-ChildItem ..\bin\release -r |
Foreach {$fn=$_.Fullname;$_} |
Where {!$_.PSIsContainer -and ($files | Where {$fn -like $_})} |
Copy-Item -Dest .\test