Powershell script to copy files based on filename - powershell

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.

Related

Powershell copy file/folder based on keyword

I want to copy folder which match with the keyword. however i want powershell read the keyword from starting point. i added my script below
if any folder name contain test at the start, script will copy the folder. but it's coping all folder even if "Test" keyword is available in the middle name. like if there is two folder
"This.is.a.Test.Folder"
"Test.this.is.a.Folder"
I want powershell copy only "Test.this.is.a.Folder"
any help please
$dest = "D:\2";
$include= #("*Test*")
Get-ChildItem $source -recurse -Force -Verbose -include $include | copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)}```
Your wildcard is meant to capture anything that contains the word Test in this case.
If you want to specifically start with the word Test followed by anything: Test*
Contrary, anything that ends with the word Test would be: *Test
$include = #( "Test*" )
Get-ChildItem $source -Include $include -Recurse -Force -Verbose |
Copy-Item -Destination {
Join-Path $dest -ChildPath $_.FullName.Substring($source.length)
}
Note, that you can use -File to filter only files and -Directory to filter only folders.

How do you do a selective copy from a directory tree using PowerShell?

I am new to PowerShell but have experience with Bash scripting and Python, but need to use PowerShell for a specific project. I thought my use-case was pretty easy, but am really having a hard time getting it to work.
I have a directory structure like this:
-- Data
-- ProjectA
-- Exception
- exception1.txt
-- Custom
- file1.txt
- file2.txt
-- ProjectB
-- Exception
- exception2.txt
-- Custom
- file3.txt
- file4.txt
What I am trying to do is copy this structure, but copy the 'Exception' directories and their contents to a different path to the rest of the files. So I want to end up with something like this:
-- NewFolder
-- ProjectA
-- Custom
- file1.txt
- file2.txt
-- ProjectB
-- Custom
- file3.txt
- file4.txt
-- ExceptionFolder
-- Exception
- exception1.txt
- exception2.txt
Notice that the contents of the exception folders are all merged into a single folder.
I have tried with doing a Copy-Item and then --exclude '*Exception*' but that only seems to work if the 'Exception' folder is at the top level. So I then tried with Get-ChildItem and pipe that into a where ($_fullname -notin '*Exception*') and pipe that into further steps to copy at each level, but that doesn't seem to work either. I've tried a few other things as well but none give me the results I'm looking for.
Not sure if it makes a difference, but I'm using PowerShell 7.1.1 on a Mac.
Any guidance on this one would be much appreciated!
Convoluted, yes, works? yes.
$source = '.\Data'
$destination = '.\New Folder'
$exceptionFolder = '.\ExceptionFolder'
$exceptionRegex = 'Exception|foobar'
$directories = Get-ChildItem $source -Recurse -Directory
$redirect = $directories | ? {$_.Name -match $exceptionRegex}
switch -Regex ($directories){
{$_.Name -match $exceptionRegex} {
# Create directory based on the regex match.
$ExceptionDestination = [io.directoryinfo]("{0}\{1}" -f $ExceptionFolder, $_.Name)
if (-not(Test-Path -Path $ExceptionDestination)){
$null = md $ExceptionDestination
}
# Copy top level files from the matched directory name into the $ExceptionDestination directory.
Copy-Item -Path ("{0}\*.*" -f $_.FullName) -Destination $ExceptionDestination -Verbose
}
}
# Skips copying of the contents of directories matching $exceptionRegex.
Copy-Item -Path $source -Destination $destination -Exclude ("*{0}*" -f $redirect).Name -Recurse -Verbose
# Actually remove directories.
Get-ChildItem $destination -Recurse -Directory | ? {$_.Name -match $exceptionRegex} | Remove-Item -Recurse -Force -Verbose
You could try Robocopy ! You have an exclude parameter.
https://pureinfotech.com/exclude-files-folders-robocopy-windows-10/
I may be wrong but, this might be what youre asking for?
#add -Recurse to copy subfolders
Get-ChildItem -Path C:\Path1 -Exclude "Exception" | Copy-Item -Destination C:\Path2
Or,
#add -Recurse to copy subfolders
Copy-Item -Path C:\path1\* -Exclude 'Exception' -Destination C:\path2
First copy all items, excluding files named like "Exception*" (there seems to be no way to exclude based on directory name, apart from a Get-ChildItem pipeline):
Copy-Item 'Data\*' -Destination 'NewFolder' -Exclude 'Exception*' -Recurse
Unfortunately this creates empty "Exception" folders in the destination, so we have to delete them:
Remove-Item 'NewFolder\*' -Include 'Exception' -Recurse
Here is an example for a Get-ChildItem pipeline in case you need a more general solution. You could change the Where-Object condition to filter on full path, e. g. $_.Fullname -notlike '*\Exception\*'.

How to copy all files in a folder to another folder and change the extension?

I have some files in a folder with no extension file. I want to copy all the files to another folder and change their extension to .txt.
I tried this code, but it still errors for creating the destination file.
$JOB = Copy-Item -Path C:\Users\XX\Documents\Folder1* -Destination "C:\Users\XX\Documents\Folder2"
Rename-Item -Path C:\Users\XX\Documents\Folder2\* -NewName *.TXT
Get-ChildItem -Path .\Folder1 -File |
ForEach-Object { Copy-Item -Path $_.FullName -Destination ".\Folder2\$($_.BaseName).txt" }
# Alternatively without ForEach-Object (see note below)
Get-ChildItem -Path .\Folder1 -File |
Copy-Item -Path $_.FullName -Destination { ".\Folder2\$($_.BaseName).txt" }
That should do the job. Keep in mind it will work only for one level, if you want recursive copy of folder structure, you'll have to modify the script slightly.
Basically, what's happening here is you find all the files and then pipe them to Copy-Item constructing destination path with BaseName property of source file (which doesn't have extension included, in comparison to Name property).
NOTE: as -Path accepts pipeline input (see docs here), you don't need to use ForEach-Object. However, it might still be useful for visibility (depending on your preferences).
Credits to #LotPings for noticing the above.

Using Remove-Item cmdlet but excluding sub-directory

I want to remove the following files from the source, however in the source there is a sub-directory that contains files with similar names. When I run the following command it is deleting files in the sub-directory with similar file name. Is there a way to just delete the files from the source and not the sub-directory?
Example: test_1_file, test_2_file, test_3_file exists in each directory, TestFolder and TestFolder/sub
$source = testfolder
remove-item -Path $source -filter test_*_file -recurse -force
It's usually easiest to pipe the output of Get-ChildItem cmdlet into Remove-Item. You then can use the better filtering of Get-ChildItem as I think -Recurse in Remove-Item has some issues. You can even use Where-Object to further filter before passing to Remove-Item
$source = testfolder
Get-ChildItem -Path $source -Filter test_*_file -Recurse |
Where-Object {$_.Fullname -notlike "$source\sub\*"} |
Remove-Item -Force
If the files to delete:
are all located directly in $source
and no other files / directories must be deleted:
Remove-Item -Path $source/test_*_file -Force
No need for -Recurse (as #Bill_Stewart notes).
Note: For conceptual clarity I've appended the wildcard pattern (test_*_file) directly to the $source path.
Using a wildcard expression separately with -Filter is generally faster (probably won't matter here), but it has its quirks and pitfalls.

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