Coping files from multiple sub folders using powershell - powershell

Copy file from multiple sub-folder to another multiple sub-folder
example :
C:\Nani\Code\Relase4\database1\tables
C:\Nani\Code\Relase1\database1\tables
C:\Nani\Code\Relase2\database1\tables
C:\Nani\Code\Relase3\cycle1\database1\tables
C:\Nani\Code\Relase1\database1.02.tables
I have .sql files in above all folders and i want to copy to
C\Build\database1\tables
if database1\tables directory is not there , i have to create it too ,
$sourceFolder = "C:\Nani\Code"
$targetFolder = "C\Build"
Get-Childitem $sourceFolder -recurse -filter "*.sql" -Exclude $exclude | %{
#If destination folder doesn't exist
if (!(Test-Path $targetFolder -PathType Container)) {
#Create destination folder
New-Item -Path $targetFolder -ItemType Directory -Force
}
Copy-Item -Path $_.FullName -Destination $targetFolder -Recurse -force
}
above code is not creating sub folders in destination ,

I have kept the script very simple for your understanding and commented the sections.
Make sure you add all the validations for paths and error handling. Else if any of the files is giving any issue, then it wont proceed and will break the loop.
Script:
#Keeping all the sources in an array
$Sources = #("C:\Nani\Code\Relase4\database1\tables",
"C:\Nani\Code\Relase1\database1\tables",
"C:\Nani\Code\Relase2\database1\tables",
"C:\Nani\Code\Relase3\cycle1\database1\tables",
"C:\Nani\Code\Relase1\database1.02.tables")
$Destination="C\Build\database1\tables\"
#Iterating each source folder
foreach($source in $sources)
{
#Getting all the sql files under an iteration folder recursively
$files=Get-ChildItem -Path $source -Filter "*.sql" -Recurse
#Iterating all the files underneath a single source folder
foreach ($file in $files)
{
#Copying the files for a single folder to the destination
Copy-Item $file.PSPath -Destination ("$Destination" + ($file.PSParentPath | Split-Path -Leaf) + '_' + $file)
}
}
Hope it helps.

Try this, I am creating each folder first before copying files into it.
$sourceFolder = "C:\Nani\Code"
$targetFolder = "C:\Build"
$sources = Get-Childitem $sourceFolder -recurse -filter "*.sql" -Exclude $exclude | Select FullName, DirectoryName
foreach ($source in $sources)
{
$Releasepath = [regex]::match($source.DirectoryName,'C:\\Nani\\Code\\Release\d').Value
$split = $Releasepath.Replace("\","\\")
$targetfolderLeaf = $source.DirectoryName -split $split | select -Last 1
$targetfolderpath = $targetFolder+$targetfolderLeaf
if (!(Test-Path $targetfolderpath -PathType Container)) {
#Create destination folder
New-Item -Path $targetfolderpath -ItemType Directory -Force
}
Copy-Item -Path $source.FullName -Destination $targetfolderpath -Recurse -force
}

Related

How to copy only updated or newer files

I am trying to create a PowerShell script to copy new and modified files from the source folder to the destination folder. I am able to copy the new file with the given script but also want to add the condition for the modified file also. Can anyone help me to achieve this.
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Desifolder = "C:\Users\parveen.kumar\Downloads\desi"
$GetFiles = Get-ChildItem -Path $Sourcefolder
$BackUpImagesFiles = (Get-ChildItem -Path $Desifolder).Name
foreach($image in $GetFiles)
{
$fileName = $image.Name;
if($BackUpImagesFiles -notcontains $fileName)
{
Copy-Item $image.FullName -Destination $Desifolder
}
}
You can use Get-Item to find if there is a file with that name already in the destination folder or not.
If not OR the file you found is older that the one in the source folder, copy the file.
Something like this:
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Destfolder = "C:\Users\parveen.kumar\Downloads\desi"
Get-ChildItem -Path $Sourcefolder -File | ForEach-Object {
# test if there already is a file with that name in the destination folder
$existingFile = Get-Item -Path (Join-Path -Path $Destfolder -ChildPath $_.Name) -ErrorAction SilentlyContinue
# if not existing or the existing file is older than the one in the source folder, do the copy
if (!$existingFile -or $existingFile.LastWriteTime -lt $_.LastWriteTime) {
$_ | Copy-Item -Destination $Destfolder -Force
}
}
Based on your comment, if you want to keep a copy of the file that was already in the destination folder, you can change to:
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Destfolder = "C:\Users\parveen.kumar\Downloads\desi"
Get-ChildItem -Path $Sourcefolder -File | ForEach-Object {
# test if there already is a file with that name in the destination folder
$existingFile = Get-Item -Path (Join-Path -Path $Destfolder -ChildPath $_.Name) -ErrorAction SilentlyContinue
# if a file already exists AND is older than the one in the source folder, do the copy
if ($existingFile -and $existingFile.LastWriteTime -lt $_.LastWriteTime) {
# rename the existing file first before you overwrite with a newer file from the source folder
# for demo, add the file's last modified date to its name
$newName = '{0}_{1:yyyy-MM-dd HHmmss}{2}' -f $existingFile.BaseName,
$existingFile.LastWriteTime,
$existingFile.Extension
$existingFile | Rename-Item -NewName $newName -Force
$_ | Copy-Item -Destination $Destfolder -Force
}
elseif (!$existingFile) {
$_ | Copy-Item -Destination $Destfolder -Force
}
}
Another way as you suggested is to Move the existing files into another backup folder instead of renaming them first:
$Sourcefolder = "C:\Users\parveen.kumar\Downloads\Source"
$Destfolder = "C:\Users\parveen.kumar\Downloads\desi"
$BackupofDestfolder = "C:\Users\parveen.kumar\Downloads\just"
# make sure the destination and backup folders exist before trying to copy or move files there
$null = New-Item -Path $Destfolder -ItemType Directory -Force
$null = New-Item -Path $BackupofDestfolder -ItemType Directory -Force
Get-ChildItem -Path $Sourcefolder -File | ForEach-Object {
# test if there already is a file with that name in the destination folder
$existingFile = Get-Item -Path (Join-Path -Path $Destfolder -ChildPath $_.Name) -ErrorAction SilentlyContinue
# if a file already exists AND is older than the one in the source folder, do the copy
if ($existingFile -and $existingFile.LastWriteTime -lt $_.LastWriteTime) {
# move the existing file first before you overwrite with a newer file from the source folder
$existingFile | Move-Item -Destination $BackupofDestfolder -Force
$_ | Copy-Item -Destination $Destfolder -Force
}
elseif (!$existingFile) {
$_ | Copy-Item -Destination $Destfolder -Force
}
}

How can I append the creation date to all files in a folder and the subfolders in PowerShell?

I have a small script that can successfully copy all the files from folders and subfolders and append the creation time, but the files in the subfolders do not have the creation time appended to their names.
How can I append the creation date to all files in a folder and the subfolders?
My current script is:
$path = "C:\test1"
$destination = "C:\test2"
Get-ChildItem -path $path | ForEach-Object{
$newname = $_.CreationTime.toString("yyyy-MM-dd") + $_.BaseName +$_.Extension
(Copy-Item -Recurse -Path $_.FullName -Destination ( Join-Path $destination $newname))
}
You were really close, but the -Recurse switch should have been on Get-ChildItem and within the loop you need to make sure the destination subfolder paths exist.
Try
$source = "C:\test1"
$destination = "C:\test2"
Get-ChildItem -Path $source -File -Recurse | ForEach-Object {
# create the new target folderpath for the copy
$targetPath = Join-Path -Path $destination -ChildPath $_.DirectoryName.Substring($source.Length)
# make sure the target path exists, if not create it
$null = New-Item -ItemType Directory -Path $targetPath -Force
# create a new filename with CreationDate prefixed
$newName = '{0:yyy-MM-dd}{1}{2}' -f $_.CreationTime, $_.BaseName, $_.Extension
# copy the file
$_ | Copy-Item -Destination (Join-Path -Path $targetPath -ChildPath $newname) -Force
}
While you could create your own recursive method to copy files and rename them as you go, it would be easier to use Copy-Item recursively and rename the files and folders afterwards:
$Source = "src"
$Destination = "dst"
Copy-Item -Recurse $Source $Destination
foreach ($Item in (Get-ChildItem -Recurse -File $Destination)) {
Rename-Item $Item ($Item.Name + "-" + $Item.CreationTime.toString("yyyy-MM-dd"))
}

Rename folders based off of newest pdf in folder

I currently have 20000+ folders that where given a random string of characters when created. I would like to rename each folder with the name of the last PDF modified within each folder. I'm definitely in over my head. The current script seems to just move the PDF and/or folder without renaming it or creating a folder with the PDF name.
Get-ChildItem -Path $SourceFolder -Filter *.pdf |
ForEach-Object {
$ChildPath = Join-Path -Path $_.Name.Replace('.pdf','') -ChildPath $_.Name
[System.IO.FileInfo]$Destination = Join-Path -Path $TargetFolder -ChildPath $ChildPat
if( -not ( Test-Path -Path $Destination.Directory.FullName ) ){
New-Item -ItemType Directory -Path $Destination.Directory.FullName
}
Copy-Item -Path $_.FullName -Destination $Destination.FullName
}
Welcome, Robert! There's a few things going on with your script:
There's a typo: $ChildPat
You don't need a FileInfo object to create the new directory, and you can't create one from a non-existent path. $Destination = Join-Path $_.Directory $_.BaseName will get the new folder name more reliably, in the unusual case where the file name has embedded '.pdf'
It doesn't get the latest PDF.
Assuming you only want to get folders that have a PDF, you should have a nested Get-ChildItem for each folder, as #Lee_Dailey recommended:
Push-Location $SourceFolder
Foreach ($dir in (Get-ChildItem *.pdf -Recurse | Group-Object Directory | Select Name )){
Push-Location $dir.Name
$NewestPDF = Get-ChildItem *.pdf | Sort-Object ModifiedDate | Select -Last 1
$Destination = Join-Path $dir.Name "..\$($NewestPDF.BaseName)"
If(!(Test-Path $Destination)){New-Item $Destination -ItemType Directory}
Copy-Item *.PDF $Destination
Pop-Location
#Remove-Item $dir.Name #uncomment to remove the old folder (is it empty?)
}

Powershell: Loop through sub-directories and move files

I'm targeting simple task.
I would like to create folder of constant name "jpg" in all subfolders of supplied root folder "D:Temp\IMG" and move all files in every subfolder with extension ".jpg" to that newly created "jpg" folder.
I thought I'll be able to solve this by myself without deep knowledge of powershell, but it seems I have to ask.
So far, I created this code
$Directory = dir D:\Temp\IMG\ | ?{$_.PSISContainer};
foreach ($d in $Directory) {
Write-Host "Working on directory $($d.FullName)..."
Get-ChildItem -Path "$($d.FullName)" -File -Recurse -Filter '*.jpg' |
ForEach-Object {
$Dest = "$($d.DirectoryName)\jpg"
If (!(Test-Path -LiteralPath $Dest))
{New-Item -Path $Dest -ItemType 'Directory' -Force}
Move-Item -Path $_.FullName -Destination $Dest
}
}
What I'm getting out of this is infinite loop of folder "jpg" creation in every subfolder.
Where is my code and logic failing here, please?
The following script would do the job.
$RootFolder = "F:\RootFolder"
$SubFolders = Get-ChildItem -Path $RootFolder -Directory
Foreach($SubFolder in $SubFolders)
{
$jpgPath = "$($SubFolder.FullName)\jpg"
New-Item -Path $jpgPath -ItemType Directory -Force
$jpgFiles = Get-ChildItem -Path $SubFolder.FullName -Filter "*.jpg"
Foreach($jpgFile in $jpgFiles)
{
Move-Item -Path $jpgFile.FullName -Destination "$jpgPath\"
}
}
This will accomplish what you are attempting, I'm pretty sure. Your original script doesn't actually recurse, despite specifying that you want it to (Get-ChildItem has some finicky syntax around that), so I fixed that. Also fixed my suggestion (I forgot that the Extension property includes the preceding dot, so 'FileName.jpg' has '.jpg' as the extension). I added in some checking, and have it throw warnings if the file already exists at the destination.
$Directory = dir D:\Temp\IMG\ -Directory
foreach ($d in $Directory) {
Write-Host "Working on directory $($d.FullName)..."
Get-ChildItem -Path "$($d.fullname)\*" -File -Recurse -filter '*.jpg' |
Where{$_.Directory.Name -ne $_.Extension.TrimStart('.')}|
ForEach-Object {
$Dest = join-path $d.FullName $_.Extension.TrimStart('.')
If (!(Test-Path -LiteralPath $Dest))
{New-Item -Path $Dest -ItemType 'Directory' -Force|Out-Null}
If(Test-Path ($FullDest = Join-Path $Dest $_.Name)){
Write-Warning "Filename conflict moving:`n $($_.FullName)`nTo:`n $FullDest"
}Else{
Move-Item -Path $_.FullName -Destination $Dest -Verbose
}
}
}

move files with specific extension to folder in higher hierarchy

All my files are in specific folders:
17\1\1\PRO
17\1\2\PRO
17\2\1\PRO
xx\xx\xx\PRO
17 is the year (so 18 for next year etc)
the first 1 is the folder specifying the case number (can be up to 100).
The second 1 is the sub parts on the case number.
That last 1 has a folder PRO in it where all data resides.
We need to move these files, but the files need to stay inside their respective "PRO" folders.
For example:
a file in 17\1\1\pro\xxx\www\ needs to go to 17\1\1\pro\movies
a file in 17\2\2\pro\xxdfsdf\eeee\ needs to go to 17\2\2\pro\movies.
The movies folder should get created if there are files to move.
I need to get a part of the full name of a file and move the file there to the "movie" folder. The problem is I do not know how to split the full name, add \movies to it and move the files there.
This is my code so far:
Get-ChildItem -Path $mypath -Recurse -File -Filter $extension | select $_Fullname |
Move-Item -Force -Destination ($_Fullname.Split("pro"))
If the destination is always "movies subdirectory of the grandparent directory of the file's directory" you can build the destination path relative to the file's location:
Get-ChildItem ... | ForEach-Object {
$dst = Join-Path $_.Directory '..\..\movies'
if (-not (Test-Path -LiteralPath $dst -PathType Container)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Move-Item $_.FullName -Destination $dst
}
If the PRO directory is your anchor you could use a regular expression replacement like this instead:
Get-ChildItem ... | ForEach-Object {
$dst = $_.Directory -replace '^(.*\\\d+\\\d+\\\d+\\PRO)\\.*', '$1\movies'
if (-not (Test-Path -LiteralPath $dst -PathType Container)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Move-Item $_.FullName -Destination $dst
}
If you don't know how many directories there are, I would do something like this:
Get-ChildItem -Path $mypath -Recurse -File -Filter $extension | ForEach-Object {
if ($_.FullName.IndexOf('\PRO\') -gt 0) {
$Destination = Join-Path -Path $_.FullName.Substring(0,$_.FullName.IndexOf('\PRO\') + 5) -ChildPath 'movies';
New-Item $Destination -ItemType Directory -ea Ignore;
$_ | Move-Item -Destination $Destination;
} else {
throw ("\PRO\ path not found in '$($_.FullName)'");
}
}
This will work fine as long as your paths only have \pro\ once. If they have it more than once like customer\pro\17\pro\17\1\1\pro\xx\yy\zz\www and you need the last index, then use $_.FullName.LastIndexOf('\pro\').
If you've got \pro\ directories both before and after the directory that .\pro\movies\ is in, well, you're in trouble. You'll probably have to find a different point of reference.
With a set of folders
17\1\1\PRO
17\1\2\PRO
17\2\1\PRO
You could try the following
$RootPaths = Get-ChildItem -Path C:\folder\*\*\*\pro
$RootPaths will then contain all 3 paths mentioned above and the code below will move all files to the appropriate directory.
ForEach( $Path in $RootPaths)
{
$Movies = Join-Path $Path -Child "Movies"
If( -not (Test-Path $Movies ) ) { New-Item -Path $Movies -ItemType Directory }
Get-ChildItem -Path $Path -Recurse -File -Filter $Extension |
Move-Item -Path $_.FullName -Destination "$( $Path )\Movies"
}
This way it doesn't matter how many levels down your files are. They always get moved to the same directory.