I have to following directory structure:
fold1
- file1
- file2
fold2
- file1
I am trying to test to see if the folders are identical, and if they arent, copy fold1 to fold2 and overwrite any different files. This is what I have tried:
$fold1 = Get-ChildItem -Recurse -path C:\fold1
$fold2 = Get-ChildItem -Recurse -path C:\fold2
$isDif = Compare-Object $fold1 $fold2
if ($isDif -eq $null) {
Write-Host "Folders Are Identical"
}
else {
Write-Host "Folders Are Different"
Copy-Item -Path $fold1.FullName -Destination $fold2.FullName -Recurse -Force
}
But when I run it, it says the folders are different, but it doesn't copy anything over. No errors or anything, it just doesn't work.
I ended up using robocopy instead:
robocopy c:\fold1 c:\fold2 /s
i, just do it
$path1 = "C:\temp2\*"
$path2 = "C:\temp3"
Copy-Item -Path $path1 -Destination $path2 -Recurse -Force -ErrorAction Continue
This is how I would recommend doing the task in powerhsell. It makes it much easier if you create path variables, that way you are not trying to insert records into a directory object.
$path1 = "C:\fold1"
$path2 = "C:\fold2"
$fold1 = Get-ChildItem -Recurse -path $path1
$fold2 = Get-ChildItem -Recurse -path $path2
$isDif = Compare-Object $fold1 $fold2
if ($isDif -eq $null) {
Write-Host "Folders Are Identical"
}
else
{
Write-Host "Folders Are Different"
ForEach($file in $fold1)
{
if($fold2 -notcontains $file)
{
Copy-Item -Path $file.FullName -Destination $path2 -Recurse -Force
}
}
}
Related
I need to make basic / or more advanced backup script that would copy items from folder A to folder B and then log what it did.
This copies the files just fine:
$source = 'path\gamybinis\*'
$dest = 'path\backup'
Get-ChildItem -Path $source -Recurse | Where-Object { $_.LastWriteTime -gt [datetime]::Now.AddMinutes(-5)
}| Copy-Item -Destination $dest -Recurse -Force
Write-Host "Backup started"
Pause
But after this I can't write the log with | Out-File, So I've tried this:
$source = 'path\gamybinis\*'
$dest = 'path\backup'
$logFile = 'path\log.txt'
$items = Get-ChildItem -Path $source -Recurse | Where-Object { $_.LastWriteTime -gt [datetime]::Now.AddMinutes(-5)
}
foreach($item in $items){
Out-File -FilePath $logFile -Append
Copy-Item -Path "$source\$item" -Destination $dest -Recurse -Force
}
Write-Host "Backup started"
Pause
This one does absolutely nothing, what exactly am I doing wrong?
(Advanced script part would be: backing up recently modified files then files should be archived to .rar/.zip, log file have to have structure that is easily readable and log file should have information which user was working on the device during the backup) - For those who are wondering.
If you can't use robocopy, in pure PowerShell code you could do this
$source = 'path\gamybinis' # no need for '\*' because you're specifying -Recurse
$dest = 'path\backup'
$logFile = 'path\log.txt'
# test if the destination path exists. If not, create it first
if (!(Test-Path -Path $dest -PathType Container)) {
$null = New-Item -Path $dest -ItemType Directory
}
Write-Host "Backup started"
Get-ChildItem -Path $source -Recurse |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-5) } |
ForEach-Object {
$_ | Copy-Item -Destination $dest -Recurse -Force
Add-Content -Path $logFile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - Copied file '$($_.FullName)'"
}
Write-Host "Backup done"
From your comments, I understand you have problems when using the -Container switch.
Below code does not use that and creates the folder structure of the copied files in the backup folder, strictly using Powershell code:
$source = 'path\gamybinis' # no need for '\*' because you're specifying -Recurse
$dest = 'path\backup'
$logFile = 'path\log.txt'
Write-Host "Backup started"
Get-ChildItem -Path $source -File -Recurse |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-5) } |
ForEach-Object {
$target = Join-Path -Path $dest -ChildPath $_.DirectoryName.Substring($source.Length)
if (!(Test-Path $target -PathType Container)) {
# create the folder if it does not already exist
$null = New-Item -Path $target -ItemType Directory
}
$_ | Copy-Item -Destination $target -Force
Add-Content -Path $logFile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - Copied file '$($_.FullName)'"
}
Write-Host "Backup done"
I have a directory with three files: .xlsx, .docx, and .txt, I also have folders in that same directory called xlsx, docx and txt. Basically trying to put each file into its corresponding folder, as a way to practice my PowerShell skills. I'm very new to PowerShell and have tried the following. I can tell its wrong, but I'm not quite sure why.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
foreach ($folder in $folders) {
if ("*$file.extension*" -like "*$folder.Name*") {
move-item $file -Destination "C:\Users\userA\Desktop\$folder.name"
}
}
}
Try the code below. With the Where-Object function, you find the corresponding file. I remove the dot because it is included in the extension otherwise.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
$folder = $folders | Where-Object { $_.Name -Like $file.Extension.Replace(".","") }
Move-Item -Path $file -Destination $folder
}
In your example, be careful how your strings are actually been interpreted. If you have "*$item.Name*" the string actually "* variable.Name*". In this case you need to use "*$($var.Name)*" in order to get the correct string.
Here are some adjustments to your approach that make it work. Breaking the -Destination parameter out to a separate variable $newpath lets you set a debug statement there so you can easily examine what it's creating.
$folders = Get-ChildItem -Directory
$files = Get-ChildItem -File
foreach ($file in $files) {
foreach ($folder in $folders) {
if ($file.extension.trim(".") -like $folder.Name) {
$newpath = ("{0}\{1}" -f $folder.FullName, $file.Name)
move-item $file -Destination $newpath
}
}
}
You could even create target folders for extensions if they do not exist yet:
$SourceFolder = C:\sample
$TargetFolder = D:\sample
Get-ChildItem -Path $SourceFolder -File |
ForEach-Object{
$DesinationFolder = Join-Path -Path $TargetFolder -ChildPath $_.Extension.TrimStart('.')
if(-not (Test-Path -Path $DesinationFolder)){
New-Item -Path $DesinationFolder -ItemType Directory | Out-Null
}
Copy-Item -Path $_.FullName -Destination $DesinationFolder -Force
}
I have a folder hierarchy with a lot of files.
I need to copy all folders and only selected files. For this purposes I write script:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
Copy-Item $files -Destination $Destination -recurse
When I execute variable $files, it returns correct path:
But when I execute Copy-Item it returns not full path:
Perhaps my approach is wrong. If so, how to copy entire folder structure, and only selected files (in this case system.serviceModel.client.config file)?
UPD1 Ok, I've found, how to copy only folders:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
Copy-Item $path $Destination -Filter {PSIsContainer} -Recurse -Force
But how to copy only selected files, preserving their location? What needs to be in $Destination variable?
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { Copy-Item -Path $_.FullName -Destination $Destination }
This code would keep the directory structure the same too
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
$fileName = "system.serviceModel.client.config"
Get-ChildItem -Path $path -Recurse | ForEach-Object {
if($_.Name -like $fileName) {
$dest = "$Destination$(($_.FullName).Replace($path,''))"
$null = New-Item $dest -Force
Copy-Item -Path $_.FullName -Destination $dest -Force
}
}
To copy the whole folder structure AND files with a certain name, below code should do what you want:
$Source = 'D:\Drop\SOA-ConfigurationManagement - Test\181'
$Destination = 'D:\test'
$FileToCopy = 'system.serviceModel.client.config'
# loop through the source folder recursively and return both files and folders
Get-ChildItem -Path $Source -Recurse | ForEach-Object {
if ($_.PSIsContainer) {
# if it's a folder, create the new path from the FullName property
$targetFolder = Join-Path -Path $Destination -ChildPath $_.FullName.Substring($Source.Length)
$copyFile = $false
}
else {
# if it's a file, create the new path from the DirectoryName property
$targetFolder = Join-Path -Path $Destination -ChildPath $_.DirectoryName.Substring($Source.Length)
# should we copy this file? ($true or $false)
$copyFile = ($_.Name -like "*$FileToCopy*")
}
# create the target folder if this does not exist
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
if ($copyFile) {
$_ | Copy-Item -Destination $targetFolder -Force
}
}
try this
$path = 'D:\Drop\SOA-ConfigurationManagement - Test\181\'
$Destination = 'D:\test\'
$files = Get-ChildItem -Path $path -Recurse -File | where Name -like "*system.serviceModel.client.config*" | %{
$Dir=$_.DirectoryName.Replace($path, $Destination)
$NewPAthFile=$_.FullName.Replace($path, $Destination)
#create dir if not exists
New-Item -Path $Dir -ItemType Directory -Force -ErrorAction SilentlyContinue
#copy file in new dir
Copy-Item $_.FullName $NewPAthFile
}
With minimal changes I'd suggest the following:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
$files | % { $_ | Copy-Item -Destination $Destination -recurse }
You can even put the whole copy on one line:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$Destination = "D:\test\"
Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { $_ | Copy-Item -Destination $Destination -recurse }
Copy-Item can find the path from the stream of input objects but it doesn't seem to be able to take a collection of System.IO.FileInfo objects as an argument to Path.
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
}
}
}
EDIT: So after some research, since what I had wasn't quite doing the trick and the advice and comments from before got me further along than I was previously, I am back with a bit more research and a more complete script.
Import-CSV C:\saveme\file_path3.csv | ForEach-Object {
Get-ChildItem $_.path -Recurse | ForEach-Object {
$split = $_.FullName -split '\\'
$DestFile = $split[1..($split.Length - 1)] -join '\'
$DestFile = "G:\Recuva2\$DestFile"
$null = New-Item -Path $DestFile -Type File -Force
If (Test-Path $DestFile) {
$i = 0
While (Test-Path $DestFile) {
$i += 1
$DestFile = $DestFile+$i
}
} Else {
$null
}
Copy-Item -Path $_.FullName -Destination $DestFile -Verbose -Force
}
}
This seems to be breaking due to an inability to find the destination directory. It seems to be breaking on this line
$null = New-Item -Path $DestFile -Type File -Force
and this line
Copy-Item -Path $_.FullName -Destination $DestFile -Verbose -Force
The common denominator here seems to be the $DestFile. I understand I am doing a good bit to the $DestFile, but I can't seem to nail down what is causing it to break.
My desired end-result here is that the folder structure be maintained when copying the specific files over in the csv list from which I am importing.
What actually seems to be happening is that it is throwing errors whenever I attempt to copy. Error text below.
Copy-Item : Could not find a part of the path 'G:\Recuva2\HR_VIOLATORS\REPORTS\REPORT_Storage2\199452\1.3.6.1.4.1.11157.2011.3.21.8.12.5.52516\1.3.51.5156.1369.20110321.1190709\1.3.51.0.7.3750462839.61413.18976.39828.11247.2380.39394'.
At line:16 char:5
+ Copy-Item -Path $_.FullName -Destination $DestFile -Verbose -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Copy-Item], DirectoryNotFoundException
+ FullyQualifiedErrorId : System.IO.DirectoryNotFoundException,Microsoft.PowerShell.Commands.CopyItem
Command
-- END EDIT
ORIGNAL POST:
Similar to but not quite the same as
Keep rename duplicate items with copy
Essentially I am being forced to grab a list of directories from a csv file and copy their contents. However the problem I am having is that some of the directories and files are duplicated.
I am attempting to use this script to import from the csv, copy the files with directory structure intact, while renaming any duplicates, ideally at the file level so that the duplicate directories are merged.
Import-CSV C:\COPYME\file_path.csv | foreach($_.path) {
ls $_.path -recurse | foreach($_) {
$SourceFile = $_.FullName
$DestinationFile = "G:\Recuva2\"+$_
If (Test-Path $DestinationFile) {
$i = 0
While (Test-Path $DestinationFile) {
$i += 1
$DestinationFile = "G:\Recuva2\"+$_+$i
}
} Else {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -verbose -Force
}
}
I am receiving an error that says that line 2 character 24 cannot be resolved to a method? What am I missing/doing wrong here?
There is a difference between the ForEach-Object cmdlet and the ForEach loop.
Import-Csv -Path 'C:\COPYME\file_path.csv' |
ForEach-Object {
ls $_.path -recurse |
ForEach-Object {
$SourceFile = $_.FullName
$DestinationFile = "G:\Recuva2\"+$_
If (Test-Path $DestinationFile) {
$i = 0
While (Test-Path $DestinationFile) {
$i += 1
$DestinationFile = "G:\Recuva2\"+$_+$i
}
} Else {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -verbose -Force
}
}
With PowerShell 4.0+ there is a .foreach() method. But it looks like you are really trying to use the alias to ForEach-Object which as iRon has pointed out in the comments does not need the ($_).
Import-CSV C:\COPYME\file_path.csv | ForEach-Object {
Get-ChildItem $_.Path -Recurse | ForEach-Object {
$SourceFile = $_.FullName
$DestinationFile = "G:\Recuva2\" + $_
If (Test-Path $DestinationFile) {
$i = 0
While (Test-Path $DestinationFile) {
$i += 1
$DestinationFile = "G:\Recuva2\" + $_ + $i
}
} Else {
New-Item -ItemType File -Path $DestinationFile -Force
}
Copy-Item -Path $SourceFile -Destination $DestinationFile -Verbose -Force
}
}
As it turned out, I was causing my own problems by trying to pass data I had already gotten. A working version of the scripting is below. This worked flawlessly for every test I put it through thus far large and small. Thank you to the answers above for contributing to my knowledge and understanding of powershell.
Import-CSV C:\COPYME\file_path.csv | ForEach-Object {
Get-ChildItem $_.path -recurse | ForEach {
$split = $_.FullName -split '\\'
$DestinationFile = $split[1..($split.Length - 1)] -join '\'
$DestinationFile = "D:\HUBICOPY\$DestinationFile"
#$DestinationFile = (Resolve-Path $DestinationFile).Path
#$null = New-Item -Path $DestinationFile -Type File -Force
If (Test-Path $DestinationFile)
{
$i = 0
While (Test-Path $DestinationFile)
{
$i += 1
$DestinationFile = $DestinationFile+$i
}
}
Else {New-Item -ItemType File -Path $DestinationFile -Force}
Copy-Item -Path $_ -Destination $DestinationFile -verbose -Force
}
}