I compare files inside a folder. In this folder some files are existing in two file formats (filename1.jpg, filename1.bmp, ...) and some files are only existing in one format.
I try to find all files which are only existing in .bmp format and delete them.
The Code I got so far is:
$jpg = Get-ChildItem "C:\..\" -File -Filter *.jpg | ForEach-Object -Process {[System.IO.Path]::GetFileNameWithoutExtension($_)}
$bmp = Get-Childitem "C:\..\" -File -Filter *.bmp | ForEach-Object -Process {[System.IO.Path]::GetFileNameWithoutExtension($_)}
Compare-Object $jpg $bmp | where {$_.SideIndicator -eq "=>"}
This lists me the files I am looking for but I have trouble deleting them. I tried some things like:
Compare-Object $jpg $bmp | where {$_.SideIndicator -eq "=>"} | ForEach-Object {
Remove-Item "C:\..\($_.FullName)"
}
but without any success. Does anyone have a hint how I could solve this?
In your foreach your variable is not a file, it is the result from the compare.
Try this:
$a = Get-ChildItem "D:\a" -File -Filter *.jpg
$b = Get-Childitem "D:\b" -File -Filter *.bmp
Compare-Object $a.BaseName $b.BaseName | where {$_.SideIndicator -eq "=>"} | foreach {
$name = $_.InputObject
$File = $b.Where({$_.BaseName -eq $name})
if ($File.Count -gt 1) {
throw "Conflict, more than one file has the name $name"
} else {
Remove-Item -Path $File.FullName
}
}
Related
im quiet new to powerhsell and I have the following goal:
My code is supposed to loop through selected subfolders and compare those. The names of the subfolders are identical in both parent folders, however the path before those selected folders are different: C:\temp\parentF1\BackUp* and C:\temp\parentF2\BackUp*
The problem that I have is that even tho I think my $vars that I use for the comparison should have a value, are NULL and I cant think of why!
$path = "subfolder1","subfolder2","subfolder3"
$excludeF1 = #(C:\temp\parentF1\BackUp\*\subfolder5)
$excludeF2 = #(C:\temp\parentF2\BackUp\*\subfolder5)
$x = 0
while($x -lt $path.Count){
$F1 = Get-ChildItem -Recurse -Path "C:\temp\parentF1\BackUp\$path[$x]" |
Where-Object {$_.FullName -notlike $excludeF1}
$F2 = Get-ChildItem -Recurse -Path "C:\temp\parentF2\BackUp\$path[$x]" |
Where-Object {$_.FullName -notlike $excludeF2}
Compare-Object -ref $F1 -dif $F2 |
Select-Object #{Label="$path[$x]";e={$_.InputObject}},`
#{n="Fundort";e={if($_.SideIndicator -like "=>") {write-output "BackUp F1"}`
elseif($_.SideIndicator -like "<="){Write-Output "BackUp F2"}}} | Out-File dif.txt
$x++
}
start .\dif.txt
also the out-file cmdlet doesnt work but that`s a dif topic
Thanks for any help in advance
This does not work as you expect:
Get-ChildItem ... -Path "C:\temp\parentF1\BackUp\$path[$x]"
When using -Path, PowerShell interprets [...] as part of its own wildcard syntax. Use -LiteralPath to prevent that.
Anything more complex than simple variable name ($var) must be enclosed in a subexpression $(...).
Similar issue:
Select-Object #{Label="$path[$x]"; ...
This can be solved by simply removing the quotation, as $path already contains strings.
Solution:
$F1 = Get-ChildItem -Recurse -LiteralPath "C:\temp\parentF1\BackUp\$($path[$x])" | ...
$F2 = Get-ChildItem -Recurse -LiteralPath "C:\temp\parentF2\BackUp\$($path[$x])" | ...
Compare-Object ... |
Select-Object #{Label=$path[$x]; ...
The above fixes your current code, but your code could be simplified like this to avoid the subexpressions:
foreach($currentPath in $path) {
$F1 = Get-ChildItem -Recurse -Path "C:\temp\parentF1\BackUp\$currentPath" |
Where-Object FullName -notlike $excludeF1
$F2 = Get-ChildItem -Recurse -Path "C:\temp\parentF2\BackUp\$currentPath" |
Where-Object FullName -notlike $excludeF2
Compare-Object -ref $F1 -dif $F2 |
Select-Object #{Label=$currentPath;e={$_.InputObject}},`
#{n="Fundort";e={if($_.SideIndicator -like "=>") {write-output "BackUp F1"}`
elseif($_.SideIndicator -like "<="){Write-Output "BackUp F2"}}} | Out-File dif.txt
}
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.
I want to create empty text files in all the subfolders which are empty. The following piece of script will list all the empty subfolders.
$a = Get-ChildItem D:\test -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Select-Object FullName
How can I iterate through the output of the above command and create empty text files in them?
There were a few too many loops in the answer you made for yourself. I offer this solution which will make an empty file in all directories that do not have files ( From your solution it is OK if they have folders so I'm keeping with that logic.)
Get-ChildItem -Recurse C:\temp |
Where-Object {$_.PSIsContainer -and ($_.GetFiles().Count -eq 0)} |
ForEach-Object{[void](New-Item -Path $_.FullName -Name "Touch.txt" -ItemType File)}
If $_.PSIsContainer is false then it won't bother with the other condition of checking for files. Also cast the output of New-Item to void to stop the output of successfully created all those new files.
The following piece of code worked for me.
$a = Get-ChildItem D:\test -recurse | Where-Object {$_.PSIsContainer -eq $True}
$path = $a | Where-Object {$_.GetFiles().Count -eq 0} | foreach {$_.FullName}
$a | Where-Object {$_.GetFiles().Count -eq 0} | foreach {$_.FullName}|ForEach-Object -Process {New-Item -Path $path -Name "testfile.txt" -Value "Test Value" -ItemType File }
How make sure.. this does not create the file in a directory, which has sub directory in it.
i.e. file should be created in a directory where there are no files and sub-directories
this worked for me .. it creates file only if the directory neither has sub-directories or files
$a = Get-ChildItem C:\tejasoft -recurse | Where-Object {$_.PSIsContainer -eq $True -and ($_.GetDirectories().Count -eq 0)}
$path = $a | Where-Object {$_.GetFiles().Count -eq 0} | foreach {$_.FullName}
$a | Where-Object {$_.GetFiles().Count -eq 0} | foreach {$_.FullName}|ForEach-Object -Process {New-Item -Path $path -Name ".keep" -Value "Test Value" -ItemType File }
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}
I am trying to get a list of all my files with a specific extension.
(...)$_.Extension -eq ".$ext"
I read extension from console to script.
My question is how to find any file with an extension of .*?
Edit:
Here's the rest of the code:
$setOfFolders = (Get-ChildItem -Path D:\ -Directory).name
Write-host "Set last write date " -ForegroundColor Yellow -NoNewline
$ostZmiana= read-host $exten = read-host "Set extensions "
ForEach ($f in $setOfFolders)
{
$saveTofile = "C:\Users\pziolkowski\Desktop\Outs\$f.txt"
Get-ChildItem -Path D:\Urzad\Wspolny\$f -Recurse | ? {$_.LastAccessTime -lt $ostZmiana -and $_.Extension -eq ".$exten"} | % {Get-acl $_.FullName} |sort Owner | ft -AutoSize -Wrap Owner, #{Label="ShortPath"; Expression= $_.Path.Substring(38)}} > $saveToFile
}
You can also use the -filter on Get-ChildItem:
Get-ChildItem -Filter "*.txt"
And you can specifiy recursion too:
Get-ChildItem -Recurse -Filter "*.txt"
The $_.Extension will see the file as, (in the case of a text file) .txt
If $ext is currently a valid extension, then .$ext would look like ..txt echoed out.
The easiest way to get a list of files with a specific extension in PowerShell is one of two, really.
C like syntax:
$myList
Get-ChildItem |`
Foreach-Object {
if ($_.Extension -eq ".txt") { $myList += $_}
}
PowerShell style:
Get-ChildItem | Where-Object {$_.Extension -eq ".txt"} | Do stuff
To get all files, as most files have an extension, just use Get-ChildItem or GCI or ls. (GCI and LS are aliases for Get-ChildItem).
To get all files with an extension, but not a specific extension:
Get-ChildItem | Where-Object {$_.Extension}
This evaluates as a bool, so true or false.
These are off the cuff, but should get you going in the right direction.