I want to count files for every folder on an E-drive, and output the folder path and file count to a text file using PowerShell (version 2).
I have found this script, but it outputs to console. How do I change it to output to a text file?
Set-Location -Path E:\
Get-ChildItem -recurse | Where-Object{ $_.PSIsContainer } | ForEach-Object{ Write-Host $_.FullName (Get-ChildItem $_.FullName | Measure-Object).Count }
I think it would be best to get an array of resulting objects where you can store both the directory path and the number of files it contains. That way, you can afterwards show it in the console and also save it to a structured CSV file you can open in Excel.
This is for PowerShell 2:
# to keep the property order in PS version < 3.0, create an
# Ordered Dictionary to store the properties first
$dict = New-Object System.Collections.Specialized.OrderedDictionary
# now loop over the folders
$result = Get-ChildItem -Path 'E:\' -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { $_.PSIsContainer } |
ForEach-Object {
# add the results in the temporary ordered dictionary
$dict.Add('Directory', $_.FullName)
$dict.Add('Files', #(Get-ChildItem -Path $_.FullName -Force -ErrorAction SilentlyContinue |
Where-Object { !$_.PSIsContainer }).Count)
# and output a PSObject to be collected in array '$result'
New-Object PSObject -Property $dict
$dict.Clear()
}
# output on screen
$result | Format-Table -AutoSize
#output to CSV file
$result | Export-Csv -Path 'D:\Test\FileCount.csv' -NoTypeInformation
The -Force switch makes sure you also count items that otherwise can't be accessed by the user, such as hidden or system files.
Get-ChildItem c:\tmp -recurse |
Where-Object{ $_.PSIsContainer } |
ForEach-Object {
"$($_.Fullname) $((Get-ChildItem $_.FullName | Where-Object{!$_.PSIsContainer}).count)"
} |
Out-File c:\tmp\out.txt
You can use the > operator for this:
Set-Location -Path E:\
(Get-ChildItem -recurse | Where-Object{ $_.PSIsContainer } | ForEach-Object{ Write-Host $_.FullName (Get-ChildItem $_.FullName | Measure-Object).Count }) >"OUTPUTFILEPATH.txt"
Related
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 am trying to use powershell to produce a list of folder names and how many files are in each folder.
I have this script
$dir = "C:\Users\folder"
Get-ChildItem $dir -Recurse -Directory | ForEach-Object{
[pscustomobject]#{
Folder = $_.FullName
Count = #(Get-ChildItem -Path $_.Fullname -File).Count
}
} | Select-Object Folder,Count
Which lists the file count, but it puts the full path (i.e. C:\Users\name\Desktop\1\2\-movi...). Is there any way to just display the last folder ("movies") as well as save the result to a .txt file?
Thank you
Instead of $_.FullName, use $_.Name to only get the directory name.
Your Select-Object call is redundant - it is effectively a no-op.
While it's easy to send the results to a .txt file with >, for instance, it's better to use a more structured format for later programmatic processing.
In the simplest form, that means outputting to a CSV file via Export-Csv; generally, however, the most faithful way of serializing objects to a file is to use Export-CliXml.
Using Export-Csv for serialization:
$dir = 'C:\Users\folder'
Get-ChildItem -LiteralPath $dir -Recurse -Directory | ForEach-Object {
[pscustomobject] #{
Folder = $_.Name
Count = #(Get-ChildItem -LiteralPath $_.Fullname -File).Count
}
} | Export-Csv -NoTypeInformation results.csv
Note that you could streamline your command by replacing the ForEach-Object call with a Select-Object call that uses a calculated property:
$dir = 'C:\Users\folder'
Get-ChildItem -LiteralPath $dir -Recurse -Directory |
Select-Object Name,
#{ n='Count'; e={#(Get-ChildItem -LiteralPath $_.Fullname -File).Count} } |
Export-Csv -NoTypeInformation results.csv
You mean something like this...
Clear-Host
Get-ChildItem -Path 'd:\temp' -Recurse -Directory |
Select-Object Name,FullName,
#{Name='FileCount';Expression = {(Get-ChildItem -Path $_.FullName -File -Recurse| Measure-Object).Count}} `
| Format-Table -AutoSize
# Results
Name FullName FileCount
---- -------- ---------
abcpath0 D:\temp\abcpath0 5
abcpath1 D:\temp\abcpath1 5
abcpath2 D:\temp\abcpath2 5
Duplicates D:\temp\Duplicates 12677
EmptyFolder D:\temp\EmptyFolder 0
NewFiles D:\temp\NewFiles 4
PngFiles D:\temp\PngFiles 4
results D:\temp\results 905
...
Every day we receive a zipfile from a number of clients. The filename consists of the following:
data_clientname_timestamp.zip
Where "data" is always the same text, "clientname" could be anything and "timestamp" is the file creation date.
The files are always in the same directory. The clientnames are always known in advance, so I know what files should be received.
The script should do the following:
List all files received (created) today
If a file from one or more clients is missing, write "file from client.. missing" to a file
I would like to list the clients in a variable, so those can easily be changed.
What I have so far:
$folder='C:\data'
Get-ChildItem $folder -recurse -include #("*.zip") |
Where-Object {($_.CreationTime -gt (Get-Date).Date )} | select name | out-file $folder\result.txt
But how to check the file for missing files?
Edit:
Testdata:
$Timestamp = (Get-Date).tostring(“yyyyMMddhhmmss”)
New-Item c:\Data -type Directory
New-Item c:\Data\Data_client1_$Timestamp.zip -type file
New-Item c:\Data\Data_client2_$Timestamp.zip -type file
New-Item c:\Data\Data_client3_$Timestamp.zip -type file
New-Item c:\Data\Data_client5_$Timestamp.zip -type file
New-Item c:\Data\Data_client6_$Timestamp.zip -type file
New-Item c:\Data\Data_client7_$Timestamp.zip -type file
exit
Script:
$folder='C:\Data'
$clients = #("client1", "client2", "client3", "client4", "client5", "client6", "client7")
$files = Get-ChildItem $folder -recurse -include #("*.zip") |
Where-Object {($_.CreationTime -gt (Get-Date).Date )}
$files | Select-Object Name | Out-File $folder\result.txt
$files | Where-Object { ($_.Name -replace '.+?_([^_]+).*', '$1') -notin $clients} | Out-File $folder\result2.txt
Start with defining a list of clients you would expect like:
$clients = #("client1", "client2")
Then retrieve all zip files and save it to a variable:
$files = Get-ChildItem $folder -recurse -include #("*.zip") |
Where-Object {($_.CreationTime -gt (Get-Date).Date )}
Export the existing files to your result.txt:
$files | Select-Object Name | Out-File $folder\result.txt
Now you can determine each missing client using the Where-Object cmdlet with a regex that grabs the clientname:
$fileClients = $files | ForEach-Object { ($_.Name -replace '.+?_([^_]+).*', '$1') }
Compare-Object $clients $fileClients | select -ExpandProperty InputObject | Out-File $folder\result2.txt
You need to have a list of your clients somewhere (such as in a CSV file named clients.csv) then you could loop through that list to check if a file is found for each client. For example:
$folder='C:\data'
$Clients = Import-CSV Clients.csv
$Files = Get-ChildItem $folder -recurse -include #("*.zip") | Where-Object {($_.CreationTime -gt (Get-Date).Date )} | select name
$Clients | ForEach-Object {
$Client = $_
$ClientCheck = $Files | Where-Object {$_ -like $Client}
If (-not $ClientCheck) {
Write-Warning "$Client is missing!"
}Else{
Write-Output $ClientCheck
}
} | out-file $folder\result.txt
Am trying to delete files older than x days and would like to know which file is being deleted.
Am using below powershell script, it doesnt work
$limit = (Get-Date).AddDays(-365)
$path = $args[0]
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force | select Name,LastWriteTime | Export-CSV -NoTypeInformation -Path $args[1]
Am passing first argument as path where files are there.
Second argument is the output file which should contain the file and date modified values of those which gets deleted.
The above code works fine for deletion, but doesnt redirects the file names and the last modified values which got deleted.
If I use below code, it only redirects the file names and last modified values but files doesnt get deleted.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | select Name,LastWriteTime | Export-CSV -NoTypeInformation -Path $args[1] | Remove-Item -Force
Using below command to run it -
./OlderFiles_Cleansing.ps1 'C:\Dev\PS' 'C:\dev\CleanedFiles_01062016.csv'
What am I missing?
Neither the Export-Csv nor the Remove-Item Cmdlet return the collection you pipe in and so make it impossible to work with the items further in the pipeline.
You can do following though - split the command:
$filesToDelete = Get-ChildItem -Path $path -Recurse -Force -Attributes !Directory | Where-Object CreationTime -lt $limit
$filesToDelete | select Name,LastWriteTime | Export-CSV -NoTypeInformation -Path $args[1]
$filesToDelete | Remove-Item -Force
Note I have improved the way of detecting that an item is a file using the
Attributes param and so could simplify the Where pipe part
I have a script right now that looks for all files certain day old and certain file extension and it deletes all of the files. This works fine and it counts fine
Then I have to delete all folders that correspond to being empty and that includes all sub folders too.
I also have to output this into a file and display each file deleted. The output would show 30 folders deleted but actually 48 were really deleted.
Now my question is i am trying to do a count of all the folders deleted. I have this script but it just counts the deepest folders not all the ones deleted.
Here is the part of the script i can not get to count
$TargetFolder = "C:\Users\user\Desktop\temp"
$LogFile = "C:\Summary.txt"
$Count = 0
Date | Out-File -filepath $LogFile
get-childitem $TargetFolder -recurse -force | Where-Object {$_.psIsContainer}| sort fullName -des |
Where-Object {!(get-childitem $_.fullName -force)} | ForEach-Object{$Count++; $_.fullName} | remove-item -whatif | Out-File -filepath $LogFile -append
$Count = "Total Folders = " + $Count
$Count | Out-File -filepath $LogFile -append
Although the sort call will correctly send each directory through the pipeline in nesting order, since they are not really being removed (remove-item -whatif), the parents will still contain their empty child directories and so will not pass the second condition (!(get-childitem $_.fullName -force)). Also note that Remove-Item does not produce any output, so the deleted directories will not appear in the log.
Adapting Keith Hill's answer to a similar question, here is a modified version of the original script that uses a filter to retrieve all empty directories first, then removes and logs each one:
filter Where-Empty {
$children = #($_ |
Get-ChildItem -Recurse -Force |
Where-Object { -not $_.PSIsContainer })
if( $_.PSIsContainer -and $children.Length -eq 0 ) {
$_
}
}
$emptyDirectories = #(
Get-ChildItem $TargetFolder -Recurse -Force |
Where-Empty |
Sort-Object -Property FullName -Descending)
$emptyDirectories | ForEach-Object {
$_ | Remove-Item -WhatIf -Recurse
$_.FullName | Out-File -FilePath $LogFile -Append
}
$Count = $emptyDirectories.Count
"Total Folders = $Count" | Out-File -FilePath $LogFile -Append
Note that -Recurse was added to the call to Remove-Item, as empty child directories will remain when using -WhatIf. Neither flag should be needed when performing an actual remove on an empty directory.
Not tested:
get-childitem $TargetFolder -recurse -force |
where-object{$_.psiscontainer -and -not (get-childitem $_.fullname -recurse -force | where-object {!($_.psiscontainer)}}|
sort fullName -des |
Where-Object {!(get-childitem $.fullName -force)} |
ForEach-Object{$Count++; $_.fullName} |
remove-item -whatif |
Out-File -filepath $LogFile -append