Copy same file to multiple destinations - powershell

I want to copy a file to multiple destinations using a script that filters through a directory and selects the newest file in the $File_path then change its name and copies it to the $destination, the script i'm using is this:
$File_path = "C:\TEMP\export\liste\Text_Utf8\"
$destination = "C:\TEMP\export\C7E001"
get-childitem -path $File_path -Filter "Ges?*.txt" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 | copy-item -Destination (join-path $destination "FRER3000CCFETES01_IN.DEV")
this only copies it to one location, is there a way to improve it to copy the same file to multiple locations? i have seen this thread but it seems different.
the other locations are as follow:
C:\TEMP\export\C7P001
C:\TEMP\export\C7F001
C:\TEMP\export\C7S001
and so on.
thank you.

Although my answer isn't very different to Peter's answer, This uses the LastWriteTime property to get the latest file and uses the FullName property of the file to copy in the Copy-Item cmdlet.
$File_path = "C:\TEMP\export\liste\Text_Utf8"
$destinations = "C:\TEMP\export\C7E001", "C:\TEMP\export\C7F001", "C:\TEMP\export\C7S001"
$fileToCopy = Get-ChildItem -Path $File_path -Filter "Ges*.txt" -File |
Sort-Object -Property $_.LastWriteTime |
Select-Object -Last 1
foreach ($dest in $destinations) {
Copy-Item -Path $fileToCopy.FullName -Destination (Join-Path -Path $dest -ChildPath "FRER3000CCFETES01_IN.DEV")
}

You can use an foreach object loop
$File_path = "C:\TEMP\export\liste\Text_Utf8\"
$destination = "C:\TEMP\export\C7E001", "C:\TEMP\export\C7P001", "C:\TEMP\export\C7F001", "C:\TEMP\export\C7S001"
$Files = get-childitem -path $File_path -Filter "Ges?*.txt" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1
$Destination | Foreach-Object {copy-item $Files -Destination (join-path $_ "FRER3000CCFETES01_IN.DEV")}

Related

Powershell: Move all files except most recently modified?

Trying to simplify one of my archiving tasks, but I'm stumped on how to go about it. Basically, I just want Powershell to search a folder for files, and move all but the most recently modified (by LastWriteTime) to a backup folder.
I've searched around for solutions to this but every answer I've come across looks for the oldest file or depends on a specific file-naming convention to work.
Basically I want it to look at this this:
E:\ProjectFolder1\EDLs\File1.prproj (modified six days ago)
E:\ProjectFolder1\EDLs\File2.prproj (modified six hours ago)
E:\ProjectFolder1\EDLs\File3.prproj (modified six seconds ago)
Identify File3.prproj as the one that's most up-to-date, and move all the other files in the directory to another folder:
E:\Deep Storage\ProjectFolder1\EDLs\File1.prproj
E:\Deep Storage\ProjectFolder1\EDLs\File2.prproj
I know how to do everything except get it to compare the LastWriteTimes. Is there a way do get PS to do this?
EDIT with code sample
Get-ChildItem $sourceDir -Include $search -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -Skip 1 | `
foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
New-Item -ItemType File -Path $targetFile -Force;
Move-Item $_.FullName -destination $targetFile -Force
}
EDIT with functional code:
$sourceDir = "E:\Test1\EDLs\"
$targetDir = "E:\Deep Storage\Test1\EDLs\"
$search = "*.prproj"
Get-ChildItem $sourceDir -Recurse -Directory | ForEach-Object {
$files = $_ | Get-ChildItem -File -Filter $search
if($files.Count -lt 2) {
return
}
$newPath = Join-Path $targetDir -ChildPath $_.FullName.Substring($sourceDir.Length)
$null = New-Item $newPath -ItemType Directory -Force
$files | Sort-Object LastWriteTime -Descending | Select-Object -Skip 1 |
Move-Item -Destination $newPath -Verbose -WhatIf
}
EDIT to show actual syntax for operating environment:
$sourceDir = "E:\Projects\Current\EDLs"
$targetDir = "E:\Deep Storage\Projects\Current\EDLs"
$search = "*.prproj"
Get-ChildItem $sourceDir -Directory | ForEach-Object {
# search only for files only 1 level under this folder
$files = Get-ChildItem $sourceDir -Filter *.prproj
# if there are at least 2 files here
if($files.Count -ge 2) {
# we dont need to create new folder here since these will go directly under
# destination folder so, we can just sort and skip first as in previous logic
$files | Sort-Object LastWriteTime -Descending | Select-Object -Skip 1 |
# then move them
Move-Item -Destination $targetDir
}
Ultimately the answer was a lot simpler than I thought it would be:
$sourceDir="E:\Test1\Test2"
# Where your files are
$targetDir="E:\Deep Storage\Test1\Test2"
# Where you want to send them
$search="*.ext"
# If applicable, what type of file you want to look for
Get-ChildItem -Path $sourceDir -Filter $search | Sort-Object |
Select-Object -SkipLast 1 | Move-Item -Destination $targetDir -Verbose -WhatIf
I use environment variables for my workflow so mine looks a little different, but this should be useful for anyone in the same situation.

powershell script not interpreting file path correctly

I have an issue where a path to a file is generated by an application. So the path looks like this….
Unfortunately, this output is generated from an application…so I cannot manipulate the output.
Now…when I run my powershell script to copy the file …I get the following errors
It seems that the problem is that my powershell script is not getting the path correctly….
$folders = Get-Content -Path '\\TFA-APP-01\CUI\WS\FOUNDFILES\results.txt'
$dest = '\\TFA-APP-01\CUI\WS\FOUNDFILES\found\'
$excludes = "WS","FOUNDFILES"
foreach ($folder in $folders) {
# Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Desktop'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Documents'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Downloads'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Outlook'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'INetCache'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Attachments'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
}
How can I either parse the results.txt file and extract just the file paths to another text file?
Or how can I modify my CopyResults.ps1 script to interpret the file path correctly?
Your issue is that Get-Content converts every line of a file into an element of an array, and you don't want to look at every line. You want to look at a specific part of a specific line that repeats in a specific pattern.
Because of all of these constants, we can make a pretty simple script to get the information you want.
$paths = Get-Content C:\temp\paths.txt
for ($i = 1; $i -lt $paths.Length; $i += 3) {
$path = "$($paths[$i].Split('.txt')[0]).txt"
$path # \\SERVER\PATH\FILE.TXT
}
Your file has a pattern of [empty line, path line, company line]. If we think about the .txt file as an array with many subarrays of that pattern, we can see that we want to get the 1st index ($i = 1) for every set of 3 ($i += 3) in the file.
Since I split by .txt, I have to string interpolate the .txt extension back on.
Edit:
Here's the script modified for your issues
$paths = Get-Content C:\temp\paths.txt
for ($i = 1; $i -lt $paths.Length; $i += 3) {
$pathSplit = $paths[$i].Split('.')
$extension = $pathSplit[1].split(' ')[0]
$path = "${$pathSplit[0]).$extension"
$path # \\SERVER\PATH\FILE.TXT
}
$pathSplit is split at the extension into 2 parts. One is the majority of the path and the other is the rest of the line.
$extension looks at the 1st index and splits by the space in order to isolate the file extension.
Finally, $path combines $pathSplit[0] and $extension to give you the full file path.

Powershell Find all empty folders and subfolders in a given Folder name

I´m trying to get a
a) list of all empty folders and subfolders if the folder is named "Archiv"
b) I´d like to delete all those empty folders. My current approch doesn´t check the subfolders.
It would be also great if the results would be exportet in a .csv =)
$TopDir = 'C:\Users\User\Test'
$DirToFind = 'Archiv'>$EmptyDirList = #(
Get-ChildItem -LiteralPath $TopDir -Directory -Recurse |
Where-Object {
#[System.IO.Directory]::GetFileSystemEntries($_.FullName).Count -eq 0
$_.GetFileSystemInfos().Count -eq 0 -and
$_.Name -match $DirToFind
}
).FullName
$EmptyDirList
Any ideas how to adjust the code? Thanks in advance
You need to reverse the order in which Get-ChildItem lists the items so you can remove using the deepest nested empty folder first.
$LogFile = 'C:\Users\User\RemovedEmptyFolders.log'
$TopDir = 'C:\Users\User\Test'
# first get a list of all folders below the $TopDir directory that are named 'Archiv' (FullNames only)
$archiveDirs = (Get-ChildItem -LiteralPath $TopDir -Filter 'Archiv' -Recurse -Directory -Force).FullName |
# sort on the FullName.Length property in Descending order to get 'deepest-nesting-first'
Sort-Object -Property Length -Descending
# next, remove all empty subfolders in each of the $archiveDirs
$removed = foreach ($dir in $archiveDirs) {
(Get-ChildItem -LiteralPath $dir -Directory -Force) |
# sort on the FullName.Length property in Descending order to get 'deepest-nesting-first'
Sort-Object #{Expression = {$_.FullName.Length}} -Descending |
ForEach-Object {
# if this folder is empty, remove it and output its FullName for the log
if (#($_.GetFileSystemInfos()).Count -eq 0) {
$_.FullName
Remove-Item -LiteralPath $_.FullName -Force
}
}
# next remove the 'Archiv' folder that is now possibly empty too
if (#(Get-ChildItem -LiteralPath $dir -Force).Count -eq 0) {
# output this folders fullname and delete
$dir
Remove-Item -LiteralPath $dir -Force
}
}
$removed | Set-Content -Path $LogFile -PassThru # write your log file. -PassThru also writes the output on screen
Not sure a CSV is needed, I think a simple text file will suffice as it's just a list.
Anyway, here's (although not the most elegant) a solution which will also delete "nested empty directories". Meaning if a directory only contains empty directorIS, it will also get deleted
$TopDir = "C:\Test" #Top level directory to scan
$EmptyDirListReport = "C:\EmptyDirList.txt" #Text file location to store a file with the list of deleted directorues
if (Test-Path -Path $EmptyDirListReport -PathType Leaf)
{
Remove-Item -Path $EmptyDirListReport -Force
}
$EmptyDirList = ""
Do
{
$EmptyDirList = Get-ChildItem -Path $TopDir -Recurse | Where-Object -FilterScript { $_.PSIsContainer } | Where-Object -FilterScript { ((Get-ChildItem -Path $_.FullName).Count -eq 0) } | Select-Object -ExpandProperty FullName
if ($EmptyDirList)
{
$EmptyDirList | Out-File -FilePath $EmptyDirListReport -Append
$EmptyDirList | Remove-Item -Force
}
} while ($EmptyDirList)
This should do the trick, should works with nested too.
$result=(Get-ChildItem -Filter "Archiv" -Recurse -Directory $topdir | Sort-Object #{Expression = {$_.FullName.Length}} -Descending | ForEach-Object {
if ((Get-ChildItem -Attributes d,h,a $_.fullname).count -eq 0){
$_
rmdir $_.FullName
}
})
$result | select Fullname |ConvertTo-Csv |Out-File $Logfile
You can do this with a one-liner:
> Get-ChildItem -Recurse dir -filter Archiv |
Where-Object {($_ | Get-ChildItem).count -eq 0} |
Remove-Item
Although, for some reason, if you have nested Archiv files like Archiv/Archiv, you need to run the line several times.

How to copy Oldest file to new directoy?

I need a script to move 1 old file to another directory.
I have a script but is not good for me:
$path = "C:*.*"
$Destination = "C:*.*"
Foreach($file in (Get-ChildItem $path)) {
If($file.LastWriteTime -gt (Get-Date).adddays(-1).date) {
Move-Item -Path $file.fullname -Destination $Destination
}
}
I need only 1 file, and Oldest every day.
Please help, Thanks
If you want only one file then you don't need a foreach loop there. You can try something like this using the Sort-Object and the Select-Object with the -Descending parameter.
Presently I have sorted that with the CreationTime and you can always choose the first element of the resultant using the -First.
$path = "C:*.*" ;
$Destination = "C:*.*" ;
$file= Get-ChildItem $path | select name,lastwritetime,CreationTime | sort-object -property CreationTime -Descending | Select-Object -First 1 ;
If($file.LastWriteTime -gt (Get-Date).adddays(-1).date) {
Move-Item -Path $file.fullname -Destination $Destination
}
Hope it helps.
try Something like this
$path = "C:\temp" ;
$Destination = "C:\temp\olddir\" ;
Get-ChildItem $path -file -rec |
where {$_.LastWriteTime -le (Get-Date).adddays(-10).date} |
Select-Object -First 1 |
Move-Item -Destination "$Destination"

Powershell compare-object

I trying to make a script which compare two directory ( source, destination) and if there are a difference on destination, copy files from source to destination.
The problem is that I don't know how copy the tree of files too.
Example:
$s = "C:\source\client"
$t = "C:\destination\client"
$target = Get-ChildItem $t -Recurse
$source = get-childitem $s -Recurse
Compare-Object $source $target -Property Name , Length |
Where-Object { $_.SideIndicator -eq '<=' } |
foreach-object -process{
copy-item $_.FullName -destination $t
}
If I have a file in source ( C:\source\client\bin\file.txt) and not in the destination folder, how is the code to copy the file in C:\destination\client\bin\file.txt ?
Thanks.
I am in the process of testing this more. From what i can see the logic of your code is sound.
Compare-Object $source $target -Property Name , Length |
Where-Object { $_.SideIndicator -eq '<=' } | Select-Object -ExpandProperty inputobject |
foreach-object -process{
copy-item $_.FullName -destination $t
}
Once you have the compare done pipe the results after the Where in Select-Object -ExpandProperty inputobject to extract the File item so that you can see the FullName property
copy-item has a -recurse parameter that will let you specify the root of a directory and then copy everything below it
copy-item c:\test d:\test -recurse -force
Edit:
The problem is for repeated tasks you can't stop it from trying to overwrite everything. You can add -force to make it do it, but it is not very efficient.
Alternatively (and probably a better and simpler way to go about this) you could call robocopy with the /mir switch
Thanks for sharing. Here is what I have done with everything I searched to compare MD5 and then copy only newly added and different files.
With [Compare contents of two folders using PowerShell Get-FileHash] from http://almoselhy.azurewebsites.net/2014/12/compare-contents-of-two-folders-using-powershell-get-filehash/
$LeftFolder = "D:\YFMS_Target"
$RightFolder = "D:\YFMS_Copy"
$LeftSideHash = #(Get-ChildItem $LeftFolder -Recurse | Get-FileHash -Algorithm MD5| select #{Label="Path";Expression={$_.Path.Replace($LeftFolder,"")}},Hash)
$RightSideHash = #(Get-ChildItem $RightFolder -Recurse | Get-FileHash -Algorithm MD5| select #{Label="Path";Expression={$_.Path.Replace($RightFolder,"")}},Hash)
robocopy $LeftFolder $RightFolder /e /xf *
Write-Host "robocopy LastExitCode: $LastExitCode"
if ($LastExitCode -gt 7) { exit $LastExitCode } else { $global:LastExitCode = $null }
Compare-Object $LeftSideHash $RightSideHash -Property Path,Hash | Where-Object { $_.SideIndicator -eq '<=' } | foreach { Copy-Item -LiteralPath (Join-Path $LeftFolder $_.Path) -Destination (Join-Path $RightFolder $_.Path) -verbose}