Test-Path returning true on empty folders - powershell

Test-Path is returning true, or at least appears to be on folders that are empty if they are located in the same directory as folders returning true that are NOT empty.
I've tried adjusting wildcard locations, adding additional \ to change paths to see if I could reduce the number of returned folders, but I cannot. I assumed the foreach portion and Test-Path would individually check each folder and return a result, but it appears once it sees contents, all folders thereafter are returned as true.
[int]$subDay = Read-Host "Enter days to subtract"
$date0 = (Get-Date).AddDays(-$subDay).ToString("yy") +
((Get-Date).AddDays(-$subDay).DayOfYear).ToString("D3")
$date1 = (Get-Date).AddDays(-$subDay).ToString("yyyy") +
((Get-Date).AddDays(-$subDay).DayOfYear).ToString()
$path0 = ".\11"
$path1 = ".\12"
$path2 = ".\13"
$a = $path0, $path1, $path2
function Split {
Param ($split)
Split-Path -Parent $split |
Get-Item |
Select-Object -ExpandProperty Name |
Add-Content $archLog
foreach ($element in $a) {
if (Test-Path $element\$date1\"$date0*"\*) {
Split $element\$date1
Split $element\$date1\"$date0*"\*
} else {
Split $element\$date1
Write-Output "Folders do not exist or are empty." |
Add-Content $archlog
}
}
I expected the code to return folder name if it had contents, which it does. However, if an empty folder exists where a folder with contents does, both are returned. If you take away the contents of the folder, none are returned.

To be able to see every subfolder that contains a file use the parameters -file and -recurse of the Get-ChildItem cmdlet:
$a = $path0, $path1, $path2
foreach ($element in $a) { Get-ChildItem -Path $element\$date1 -file -Recurse | foreach {$_.Directory.Name} | select -uniq }

Related

Find similarly-named files, and if present, remove the files without a specific string using PowerShell

In a directory, there are files with the following filenames:
ExampleFile.mp3
ExampleFile_pn.mp3
ExampleFile2.mp3
ExampleFile2_pn.mp3
ExampleFile3.mp3
I want to iterate through the directory, and IF there is a filename that contains the string '_pn.mp3', I want to test if there is a similarly named file without the '_pn.mp3' in the same directory. If that file exists, I want to remove it.
In the above example, I'd want to remove:
ExampleFile.mp3
ExampleFile2.mp3
and I'd want to keep ExampleFile3.mp3
Here's what I have so far:
$pattern = "_pn.mp3"
$files = Get-ChildItem -Path '$path' | Where-Object {! $_.PSIsContainer}
Foreach ($file in $files) {
If($file.Name -match $pattern){
# filename with _pn.mp3 exists
Write-Host $file.Name
# search in the current directory for the same filename without _pn
<# If(Test-Path $currentdir $filename without _pn.mp3) {
Remove-Item -Force}
#>
}
enter code here
You could use Group-Object to group all files by their BaseName (with the pattern removed), and then loop over the groups where there are more than one file. The result of grouping the files and filtering by count would look like this:
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1
Count Name Group
----- ---- -----
2 ExampleFile {ExampleFile.mp3, ExampleFile_pn.mp3}
2 ExampleFile2 {ExampleFile2.mp3, ExampleFile2_pn.mp3}
Then if we loop over these groups we can search for the files that do not end with the $pattern:
#'
ExampleFile.mp3
ExampleFile_pn.mp3
ExampleFile2.mp3
ExampleFile2_pn.mp3
ExampleFile3.mp3
'# -split '\r?\n' -as [System.IO.FileInfo[]] | Set-Variable files
$pattern = "_pn"
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1 | ForEach-Object {
$_.Group.Where({-not $_.BaseName.Endswith($pattern)})
}
This is how your code would look like, remove the -WhatIf switch if you consider the code is doing what you wanted.
$pattern = "_pn.mp3"
$files = Get-ChildItem -Path -Filter *.mp3 -File
$files | Group-Object { $_.BaseName.Replace($pattern,'') } |
Where-Object Count -GT 1 | ForEach-Object {
$toRemove = $_.Group.Where({-not $_.BaseName.Endswith($pattern)})
Remove-Item $toRemove -WhatIf
}
I think you can get by here by adding file names into a hash map as you go. If you encounter a file with the ending you are interested in, check if a similar file name was added. If so, remove both the file and the similar match.
$ending = "_pn.mp3"
$files = Get-ChildItem -Path $path -File | Where-Object { ! $_.PSIsContainer }
$hash = #{}
Foreach ($file in $files) {
# Check if file has an ending we are interested in
If ($file.Name.EndsWith($ending)) {
$similar = $file.Name.Split($ending)[0] + ".mp3"
# Check if we have seen the similar file in the hashmap
If ($hash.Contains($similar)) {
Write-Host $file.Name
Write-Host $similar
Remove-Item -Force $file
Remove-Item -Force $hash[$similar]
# Remove similar from hashmap as it is removed and no longer of interest
$hash.Remove($similar)
}
}
else {
# Add entry for file name and reference to the file
$hash.Add($file.Name, $file)
}
}
Just get a list of the files with the _pn then process against the rest.
$pattern = "*_pn.mp3"
$files = Get-ChildItem -Path "$path" -File -filter "$pattern"
Foreach ($file in $files) {
$TestFN = $file.name -replace("_pn","")
If (Test-Path -Path $(Join-Path -Path $Path -ChildPath $TestFN)) {
$file | Remove-Item -force
}
} #End Foreach

Ignoring subdirectories in powershell

I have a line of code that prints out all the files and folders with that are similar to $filename e.g. keyword "abc" will also include a file/folder "abcdef"
Get-ChildItem -Path 'C:\' -Filter $filename -Recurse | %{$_.FullName}
I'd like to have make it so that the search for these files does not go into the sub-directories of folders
e.g. a folder with name "abc" and subfolder "abcdef" only prints out "C:\abc"
Currently the line of code would print out "C:\abc" and "C:\abc\abcdef"
What would be the best way to do this?
This will do it.
Get-ChildItem is performed at the top level to populate the processing queue ($ProcessingQueue)
Then, a loop will run until the processing queue does not have any element left.
Each element in the queue will undergo the same process.
Either it match the filter, in which case it will be added to the $Result variable or it does not, in which case Get-ChildItem will be called on that directory and its result appended to the queue.
This ensure we do not process any further a directory tree once we have a match and that that the recursion is only applied if the directory did not match the folder in the first place.
--
Function Get-TopChildItem($Path, $Filter) {
$Results = [System.Collections.Generic.List[String]]::New()
$ProcessingQueue = [System.Collections.Queue]::new()
ForEach ($item in (Get-ChildItem -Directory $Path)) {
$ProcessingQueue.Enqueue($item.FullName)
}
While ($ProcessingQueue.Count -gt 0) {
$Item = $ProcessingQueue.Dequeue()
if ($Item -match $Filter) {
$Results.Add($Item)
}
else {
ForEach ($el in (Get-ChildItem -Path $Item -Directory)) {
$ProcessingQueue.Enqueue($el.FullName)
}
}
}
return $Results
}
#Example
Get-TopChildItem -Path "C:\_\111" -Filter 'obj'

How do I create a new files automatically depend on an existing variable in PowerShell?

I have many file in a folder, I would like to check the existing and matching of the file with variable that I initialize. Then, if the file exit and match, I want to get some information from the files (many file), then create a new file depend on how many file exist and match.
I tried this code, I can check the matching and existing file. I can create a new file and get the information from the file, but I only can create 1 file.
The information that I get from the file, each file is different.
$ID = "123"
$Pre = "ABC"
$Path = "C:\Folder"
$PO = Get-ChildItem -Path $Path
foreach ($File in $PO) {
if (($File.Name -match $ID) -and ($File.Name -match $Pre)) {
Write-Host ">>POfile Found: $File"
} else {
Write-Host ">>Check Again!"
}
}
# CREATE FILE
$Jb_Path = "C:\Folder\Jb"
## GET INFORMATION
$count = 1
$Get_PO = Get-ChildItem -Path $Path\$File -File -Recurse
$POfile = Get-Random -InputObject $Get_PO -Count $count
Write-Host ">>Selected POfile= $POfile"
$FilteredContents = Get-Content $POfile | Where-Object {$_ -like "*;INFO*"}
$Get_INFO = $FilteredContents.Substring(5,2)
## NEW FILE
New-Item -Path $Jb_Path\NEW_$Pre$ID-$Get_INFO.txt -Force
In the section # CREATE FILE you are referencing the variable $File which has the last value iterated in the previous foreach (even if it didn't match the if condition).
Asuming the $Pre is for prefix and comes first in a file name simply do a
Get-ChildItem "$Path\*$Pre*$ID*"
to only get file names for your criteria.
As $File contains only one file name a Get-Random doesn't make sense, especially as it might not contain a line with ;INFO
Assuming the two characters to extract are in front of ;INFO this untested script might do:
$Pre = "ABC"
$ID = "123"
$Path = "C:\Folder"
$Jb_Path= "C:\Folder\Jb"
Get-ChildItem "$Path\*$Pre*$ID*" | Get-Content |
Select-String -Pattern '^.....(..).*;INFO' |
Get-Random | ForEach-Object {
$NewFile = Join-Path $Jb_Path ('NEW_{0}{1}-{2}.txt' -f $Pre,
$ID,$_.Matches.Groups[1].Value)
New-Item -Path $NewFile -ItemType File -Force -WhatIf
}
It will only output what it would do without the -WhatIf parameter.
If no file matching the criteria and RegEx pattern is found it will silently continue.
If my assumptions led me wrong, enhance your question be editing it with more details.

Identify Empty Folders

I would like to identify a specific empty folder in our user profiles.
I have a text file containing all of our user names that I want the script to refer to. The script will loop each user directory and either output to file or screen and say if the directory is empty. Hidden files do not have to count!
Something similar
FOR /F %U IN (C:\UserList\UserList.TXT) DO *Find and List Empty Folder* \\Server\Share\%U\Target_Folder
Powershell solutions welcome!
This article on Technet provides the following Powershell code snippet to identify all empty folders:
$a = Get-ChildItem C:\Scripts -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Select-Object FullName
Replace "C:\Scripts" with the root folder you want to search.
Update:
The following script will get you in the ballpark.
$content = Get-Content C:\Temp\FolderList.txt
foreach ($line in $content)
{
Write-Host $line -NoNewline
$testObject = Test-Path -Path $line
if ($testObject)
{
$folder = Get-Item -Path $line
$filesCount = $folder.GetFiles().Count
if ($filesCount.Equals(0))
{
Write-Host " - Empty folder"
}
else
{
Write-Host " - Contains files"
}
}
else
{
Write-Host " - Invalid path"
}
}

Compare directory with sub-folders

I have been have a tough time with this but I am trying to do a compare between two servers and have them compare all of the files from one server to the other. Even inside the subfolders. So far I've gotten it to compare the folder names and files but can't get it to go inside the folders and compare the contents. Here's a few things I've tried.
$A = Get-ChildItem -Recurse -path $Path
$B = Get-ChildItem -Recurse -path $PAth1
Compare-Object -ReferenceObject $A -DifferenceObject $B -PassThru
This is what I started with and it works the best but still doesn't go inside the sub-folders. I also tried to use foreach statements with Arrays to store the content in an array and compare the arrays but this doesn't seem to be working at all.
$FileDirectoryA = "Path"
$FileDirectoryC = "path"
$A = foreach($folderA in Get-ChildItem $fileDirectoryA)
{
$FolderA
}
$B = foreach($FileA in Get-ChildItem $ArrayA)
{
$FileA
}
$C = foreach($folderC in Get-ChildItem $fileDirectoryC)
{
$FolderC
}
$D = foreach($FileC in Get-ChildItem $ArrayC)
{
$FileC
}
Compare-Object $B $D
When I try and just do a
Compare-Object $Path $Path2
It errors on all of the folders saying permission denied which doesn't make much sense to me.
Thanks,
Andrew
It should work if you specify the property to compare :
$a = ls c:\temp\ -rec
$b = icm -computername $servername -scriptblock{ls c:\temp\ -rec}
compare $a $b -Property fullname
A more reliable way might be to use robocopy
robocopy.exe \\serverA\Folder1 \\serverB\Folder2 /e /l /log:c:\temp\dif.txt
I think this powershell script https://github.com/oflisback/dirdiff addresses your problem, it scans two directories and reports differences:
PS C:\> dirdiff.ps1 C:\dir1 C:\dir2
One identical file:
.\identical.txt
One file only present in C:\dir1:
.\newdir1.txt
2 files only present in C:\dir2:
.\newdir2.txt
.\newdir\newfilesdir2.txt
One file present in both directories and has different content:
.\modified.bin
The script is based on this gist so credit to cchamberlein.
Get-ChildItem $Source -Recurse | ForEach-Object{
$Dest = Join-Path $Destination $_.FullName.Substring($Source.Length)
$bool = Test-Path $Dest
if(!$bool){
#Write of some thing here to indicate difference
}
}
This should give you a reasonable comparison I think. You can use regex on the return values to rebuild the original filepaths if you so desire.
$Dir1="C:\Test"
$Dir2="C:\Test2"
$A=(Get-ChildItem $Dir1 -Recurse).VersionInfo.Filename -replace [regex]::Escape($Dir1),""
$B=(Get-ChildItem $Dir2 -Recurse).VersionInfo.Filename -replace [regex]::Escape($Dir2),""
Compare-Object $A $B
This reuslts in an output like...
InputObject SideIndicator
----------- -------------
\New folder\temp.txt =>
\temp.txt <=
\New folder\New folder\temp.txt <=
Update
I think should do everything you are looking for. You'll need to change the $Dir1 and $Dir2 values. It's likely over-complicated but it does appear to work.
$Dir1="C:\Test"
$Dir2="C:\Test2"
$Items1=Get-ChildItem $Dir1 -Recurse
$Items2=Get-ChildItem $Dir2 -Recurse
$A=$Items1.VersionInfo.Filename -replace [regex]::Escape($Dir1),""
$B=$Items2.VersionInfo.Filename -replace [regex]::Escape($Dir2),""
$Exist_Diff=Compare-Object $A $B
$Exist_Diff | %{
$Current_Item=$_.InputObject
if($_.SideIdicator -eq "<="){
Write-Host "No file equivilent to $Dir1$Current_Item found in $Dir2"
}else{
Write-Host "No file equivilent to $Dir2$Current_Item found in $Dir1"
}
}
$Items1 | %{
if ($Exist_Diff.InputObject -notcontains ($_.VersionInfo.Filename -replace [regex]::Escape($Dir1),""))
{
$MatchingFile=[string]$_.VersionInfo.Filename -replace [regex]::Escape($Dir1),$Dir2
try{if(Test-Path $MatchingFile)
{
if((Get-Item $MatchingFile).length -gt $_.Length)
{
Write-host $_.fullname "is larger than" $MatchingFile
}
}}catch{}
}
}
$Items2 | %{
if ($Exist_Diff.InputObject -notcontains ($_.VersionInfo.Filename -replace [regex]::Escape($Dir1),""))
{
$MatchingFile=[string]$_.VersionInfo.Filename -replace [regex]::Escape($Dir2),$Dir1
try{if(Test-Path $MatchingFile)
{
if((Get-Item $MatchingFile).length -gt $_.Length)
{
Write-host $_.fullname "is larger than" $MatchingFile
}
}}catch{}
}
}