Copy file from a directory based on CSV file - powershell

I have some issues now concerning a code in PowerShell.
I want to write a script in PowerShell to copy files from one folder to another existing folder based on CSV file :
For example I have in my CSV two columns : one for the name of the file without the extension and one for the path where I want to copy my file into.
example of csv :
File,Pathdestinationfile
test1_000,C:/Documents/test1
so I need to get my file from a path such as C:/Source and copy it to the specific destinationfile path mentionned in my CSV.
Here is the code that I have for the moment but it doesn't work :
Import-CSV C:\Users\T0242166\Documents\SCRIPT_CSV\testscript.csv |
Where-Object Name -Like $_.File |
foreach { Copy-item -Path "C:/Source" -Destination $_.Filepathdestination -Recurse }
Can you please help me with this issue ?
Thank you

I have provided comments in the script to help you understand what I am doing.
(note: this can be condensed, but I separated it out so you can see it easier.)
# Define your constant variable
$sourceFolder = "C:\Source"
# Import Your CSV
$content = Import-Csv "C:\Users\T0242166\Documents\SCRIPT_CSV\testscript.csv"
# Iterate Through Your Objects From CSV
foreach ($item in $content)
{
# Find the file you are looking for
$foundFile = Get-Item -Path ($sourceFolder + "$($item.File).*")
# Copy the file using the FullName property, which is actually the full path, to your destination defined in your csv file.
Copy-Item $foundFile.FullName -Destination ($item.Pathdestinationfile)
}
Condensed Version:
# Import and Iterate Through Your Objects From CSV
foreach ($item in (Import-Csv "C:\Users\T0242166\Documents\SCRIPT_CSV\testscript.csv"))
{
# Find the file you are looking for and copy it
Copy-Item "C:\Source\$($item.File).*" -Destination $item.Pathdestinationfile
}
Another Condensed Version:
Import-Csv "C:\Users\T0242166\Documents\SCRIPT_CSV\testscript.csv" | foreach { Copy-Item "C:\Source\$($_.File).*" -Destination $_.Pathdestinationfile }

Although you keep using forward slashes in the file paths, in PowerShell this would work:
# import the data from the CSV file and loop through each row
Import-CSV -Path 'C:\Users\T0242166\Documents\SCRIPT_CSV\testscript.csv' | ForEach-Object {
# get a list of FileInfo objects based on the partial file name from the CSV
# if you don't want it to find files in subfolders of "C:\Source", take off the -Recurse switch
$files = Get-ChildItem -Path "C:\Source" -Filter "$($_.File).*" -File -Recurse
foreach ($file in $files) {
$file | Copy-Item -Destination $_.Pathdestinationfile
}
}

Related

Removing parts of filenames in Powershell from a file list

I have a bunch of directories with files in them and my goal is to loop through a directory structure and write each of the files one by one to a new location.
The files on disk look like this:
- DATAFILE1_DATE_20210101_RUNDATE_20210101.csv
- DATAFILE1_DATE_20210102_RUNDATE_20210102.csv
- DATEFILE2_DATE_20210103_RUNDATE_20210103.json
- DATEFILE2_DATE_20210104_RUNDATE_20210104.json
I'm trying to pass the contents of the directory to a variable $fileSystemItems and then to remove everything after _DATE so that I could build a new directory structure in the target as:
- /target/DATAFILE1/DATAFILE1_DATE_20210101_RUNDATE_20210101.csv
- /target/DATAFILE1/DATAFILE1_DATE_20210102_RUNDATE_20210102.csv
- /target/DATAFILE2/DATEFILE2_DATE_20210103_RUNDATE_20210103.json
- /target/DATAFILE2/DATEFILE2_DATE_20210104_RUNDATE_20210104.json
The PS code I have so far takes the files from a specified directory and outputs them:
$sourcePath = "\\files\data"
$fileSystemItems = Get-ChildItem $sourcePath -Recurse | Where { ! $_.PSIsContainer }
foreach ($file in $fileSystemItems) {
Write-Host "Writing name of the file is $($file.BaseName)"
}
I have tried using the Rename-Item and regex but renaming the files the source is not an option as other programs are accessing the same data, for example:
Get-ChildItem $sourcePath -Recurse | Rename-Item -NewName { $_.Name -replace "^_DATE+","" }
How do I modify the filename in the $file variable in the foreach loop to output both a edited version of the file name (e.g. DATEFILE1) and also the full file name DATAFILE1_DATE_20210101_RUNDATE_20210101.csv ?
Rename-Item is a great tool to begin with! First, let's optimize your code snippet a little bit:
Instead of piping Get-ChildItems output to Where { ! $_.PSIsContainer }, we'll use the -File switch.
Let's also change your regex from ^_DATE+ to _DATE.* and use a pipeline with ForEach-Object:
Get-ChildItem $sourcePath -Recurse -File | ForEach-Object {
Rename-Item -Path $_.Fullname -NewName (($_.Name -replace "_DATE.*","") + $_.Extension)
}
Looking at your destination structure it looks like you might also want to move files to a target directory.

Copy files into newly created folders on partial filename match

Hi all reaching out because I've reached the limits of my powershell knowledge.
I have a directory that has over 200,000 files, I need to copy all files that have a partial match to the filename into folders that I have already created using this script
Set-Location "C:\Users\joshh\Documents\Testing Environment"
$Folders = Import-Csv "C:\Users\joshh\Documents\Weichert.csv"
ForEach ($Folder in $Folders) {
New-Item "myfilepathhere\$($Folder.folderName)" -type directory
}
UPDATED:
Here is a sample of the filenames:
TH-246-02050-LOL-SQ-ALT2.png
TH-246-02050-WHT-H.png
TH-247-02050-EMB-LOD.png
TH-246-02050-LOL-H-ALT2.png
TH-246-02050-LOL-SQ.png
TH-246-02050-LOL-SQ-ALT.png
TH-247-02050-EMB-LOD-ALT.png
TH-247-02050-EMB-LOL.png
TH-247-02050-EMB-LOL-ALT.png
TH-247-02050-LOD-H.png
Above is an example of what the filenames look like, I need to copy all files containing -EMB- and move them into folders in another directory that match the first 12 characters of that filename (ex. TH-247-02050)
UPDATED:
And if a folder doesn't exist create a folder with the first 12 characters of the filename.
Mind you the first 12 characters have many variants some start with RM, KW, etc.
This is what I have so far and what I know but I know the Move-Item portion isn't exactly what I want it to do
$source = "targetPath"
$destination = "targetPath2"
$embFiles = #(Get-ChildItem ${source}/*EMB* -File | Select-Object -ExpandProperty FullName)
foreach($file in $embFiles) {
if($file | Where-Object { $_ -clike "*EMB*" }){
Move-Item -Path $source -Destination $destination
}
}
Any and all help would be GREATLY appreciated!
Here is one way you could do it:
Get all files that contain -EMB- in their names: -Filter *-EMB-* -File.
Group all this files by everything before -EMB-, here we can use Group-Object -AsHashTable and a calculated expression using Regex.Match. See https://regex101.com/r/iOoBJS/1 for details.
Loop through the Keys of the hash table, each Key will be the Name Destination folder of the group of files (i.e.: TH-247-02050).
Join the destination path ($destinationPath2) with the name of the destination folder ($folder), here we can use Join-Path and check if this joined path exists, if it doesn't, create a new folder with New-Item.
Lastly, we can move all the files (the Values of each Key from the hash table) to their corresponding destination.
$source = "targetPath"
$destination = "targetPath2"
$map = Get-ChildItem $source -Filter *-EMB-* -File | Group-Object -AsHashTable -AsString {
[regex]::Match($_.BaseName, '(?i).+(?=-EMB-)').Value
}
foreach($folder in $map.Keys) {
$d = Join-Path $destination -ChildPath $folder
$d = New-Item $d -ItemType Directory -Force
# -WhatIf can be removed once you have checked the script is doing what you want
$map[$folder] | Move-Item -Destination $d -WhatIf -Verbose
}
-AsString is needed in Windows PowerShell due to a bug.

copy matching files from a drive to a folder based on fodlername in powershell

I have a a bunch of language folders present in a directory under E:\Data\ like hu-hu, de-de etc.. on the other hand i have a bunch of file names in G:\ that contain the part of folder name for e.g.
amd64.de-de_OCR.cab,amd64.handwriting.de-de.cab
I need to copy all matching file names based on the foldername
for e.g. de-de should copy all matching files in G:\ i.e. both amd64.de-de_OCR.cab,amd64.handwriting.de-de.cab
This is the code i have so far but it is not copying over the files, and i am not sure how to proceed next, any help is appreciated.
$listfoldername = Get-ChildItem -Path "E:\Data" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object Name
$destfolder = Get-ChildItem -Path "E:\Data" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object FullName
$filename = Get-ChildItem -file G:\
if($filename -like $listfoldername)
{
Copy-Item -Path $filename -Destination $destfolder
}
There's a few issues with your code
The main issue with your code is that you are trying to use the -like operator to compare two objects (your object containing the directories you wish to move files to, and the object containing the files.
What you need to do is loop through each file and directory, one by one, to determine if the directory name (e.g. "hu-hu" is found in the filename (e.g. amd64.hu-hu_OCR.cab)
You'll want to use the wildcard indicator "*" with the -like operator (e.g. "*hu-hu*")
This below code snippet should do the trick. I tested using the file and folder names you've provided.
"G:" contains the folders:
de-de
hu-hu
us-us (note, I added this to make sure the code did not match this directory)
"E:\Data" contains the files
amd64.de-de_OCR.cab
amd64.handwriting.de-de.cab
amd64.handwritinghu-hu.cab
amd64.hu-hu_OCR.cab
$FileDirectory = "G:" # Change to "G:\", the trailing slash breaks syntax highlight on SO
$DataDirectory = "E:\Data"
$listfoldername = Get-ChildItem -Path "$DataDirectory" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object Name
$filename = Get-ChildItem -file "$FileDirectory"
#Loop through each file one at a time
foreach ($file in $filename) {
# Then, loop through each folder one at a time
foreach ($folder in $listfoldername) {
# Set the current filename and listfoldername to variables for later -like operator
$FileString = $file.Name
$FolderString = $folder.Name
# If the current file "is like" the current folder name
if($FileString -like "*$FolderString*")
{
# Set the name of the current folder to a variable
$DataFolder = $folder.Name
Copy-Item -Path "$FileDirectory\$FileString" -Destination "$DataDirectory\$DataFolder"
} else {
Write-Output ("$FolderString pattern not found in $FileString")
}
}
}
I think you should start off by getting a list of possible language target folders. Then loop over the path where the files are, filtering their names to have at least the dash in it and next test if any of the language target folders matches the filename.
Something like this:
$langFolder = 'E:\Data'
$fileFolder = 'G:\' #'# dummy comment to fix syntax highlighting in SO
# get a list of the language folders
# if the languages folder has multiple subdirectories to include, add -Recurse here
$targetFolders = Get-ChildItem -Path $langFolder -Directory
# get a list of FileInfo objects for the files in the G:\ path
# if you need to search subdirectories aswell, add -Recurse here
$files = Get-ChildItem -Path $fileFolder -File -Filter '*-*.*'
foreach($file in $files) {
# check if a language name matches the file name
foreach($folder in $targetFolders) {
if ($file.BaseName -like "*$($folder.Name)*") {
# we have found a matching language target directory
$file | Copy-Item -Destination $folder.FullName
break # exit this folder foreach loop and get on with the next file
}
}
}
P.S. If all the files are .cab files you could speed up by setting the Filter to '*-*.cab' in line $files = Get-ChildItem ...

Print a substring from many files to a single text File

I need to extract the 20th to 30th letters from many files in a single folder and print the results to a text file.
I understand i need to use substring(20,10) but fail at managing multiple files.
I created a working script for single file use:
$var = Get-Content -Path C:\testfiles\1.txt
$var.Substring(20,10)
But now i need to handle multiple files.
Any help?
This might work
#Path to directory
$DirPath = "C:\Files"
#get all text files from above path
$Files = Get-ChildItem -Path $DirPath -Filter "*.txt"
#path to text file where strings are stored
$DesFile = "C:\String.txt"
#loop through all files and save to desfile
foreach ($txt in $Files)
{
$var = Get-Content -Path $txt.FullName
$var.Substring(20,10) | Add-Content -Path $DesFile
}

Is it possible to increase the speed of this search and copy snippet?

I've got this script to search through a remote server for specific files I've listed in a CSV. However the process is very slow and I'm wondering if it's my code that makes it so. Can I improve it somehow? I've thought that simply making a directory list and searching it for the files in my CSV might be faster to avoid recursively looking through every file.
$source = "\\server1\Scanning_Maps"
$destination = "\\server1\Transfer\TEST"
$searchFiles = Import-CSV 'C:\Users\user1\Desktop\test.csv' -Header ("filename")
ForEach($File in $searchFiles)
{
Get-ChildItem -Path $source -Recurse | Where-Object { $_.Name -match $File.filename } | Copy-Item -Destination $destination
}
You would need to do some testing but on top of jisaak's findings you can reduce this even further by only returning files you want to copy as supposed to all files and then post processing.
I've thought that simply making a directory list and searching it for the files in my CSV might be faster to avoid recursively looking through every file.
Using -Include you can pass it the array of filenames and it should only search for and return the files that you want.
Also if you are adding the header anyway I going to guess that you have only a one column csv? If that is the case you don't even need to bother with that cmdlet and only use Get-Content.
$searchFiles = Get-Content 'C:\Users\user1\Desktop\test.csv'
If that is a CSV file and you are using header to just work the first column then ignore that tidbit. If it is a csv we should at least expand the column so you just have a string array instead of an object one.
$searchFiles = Import-CSV 'C:\Users\user1\Desktop\test.csv' -Header ("filename") | Select-Object -ExpandProperty filename
Now lets try using -Include. Note that it only works with -Recurse and might do nothing without it.
Get-ChildItem -Path $source -Include $searchFiles -Recurse | Copy-Item -Destination $destination
There, no more foreach loop required. That should be even faster.
You run the Get-ChildItem cmdlet for every file in $searchFiles you should put it outside the foreachloop:
$source = "\\server1\Scanning_Maps"
$destination = "\\server1\Transfer\TEST"
$searchFiles = Import-CSV 'C:\Users\user1\Desktop\test.csv' -Header ("filename")
$sourceList = Get-ChildItem -Path $source -Recurse
ForEach($File in $searchFiles)
{
$sourceList | Where-Object { $_.Name -match $File.filename } | Copy-Item -Destination $destination
}