Get directory name, folder name and count of all files - powershell

I am trying to create a script to read each folder name in a directory, count of zip files in each folder and then count of files in each zip. The output need to be written in an output file.
I came up with below:
$ZipRoot = 'C:\Users\Main Folder'
$ZipFiles = Get-ChildItem -Path $ZipRoot -Recurse -Filter '*.zip'
$Shell = New-Object -ComObject Shell.Application
$Results = foreach( $ZipFile in $ZipFiles ){
$FileCount = $Shell.NameSpace($ZipFile.FullName).Items() |
Measure-Object |
Select-Object -ExpandProperty Count
[pscustomobject]#{
FullName = $ZipFile.FullName
FileCount = $FileCount
}
}
$Results |
Export-Csv -Path 'C:\Users\mlkstq\Desktop\FFNS\ZipReport.csv' -NoTypeInformation
Output
Fullname Filecount
C:\Users\Main Folder\Subfolder1\Zip1 3
C:\Users\Main Folder\Subfolder2\Zip2 5
The problem is that I am having trouble getting the Subfolder name in putput file. Also want to substring subfolder name to get valid name. Whatever i try it fails.

If I've got you right I'd do it this way:
$ZipRoot = 'C:\Users\Main Folder'
$Shell = New-Object -ComObject Shell.Application
$subFolderList = Get-ChildItem -Path $ZipRoot -Recurse -Directory
$Result = foreach ($subFolder in $subFolderList) {
$zipFileList = Get-ChildItem -Path $subFolder.FullName -File -Filter *.zip
foreach ($ZipFile in $zipFileList) {
[PSCustomObject]#{
subFolder = $subFolder.FullName
zipFilesCount = $zipFileList.Count
zipFile = $ZipFile.Name
fileCount = $Shell.NameSpace($zipFile.FullName).Items().Count
}
}
}
Format-Table -InputObject $Result -AutoSize -InputObject $Result
Export-Csv -InputObject $Result -Path 'C:\Users\mlkstq\Desktop\FFNS\ZipReport.csv' -NoTypeInformation
In my opinion it just does not look that good to have the count of the zip files per subfolder repeated for each line of the subfolder

Related

How can I get the time and date my PowerShell script deletes a file

I am using the following script to read a list of file names which are then deleted. Is there a way can get an output of the date and time each file is deleted?
$targetFolder = "D:\" $fileList = "C:\DeleteList.txt" Get-ChildItem
-Path "$targetFolder\*" -Recurse -Include #(Get-Content $fileList) | Remove-Item -Verbose
Thanks for any help.
You could keep track of the files that are deleted and the time of deletion by outputting an object with the file's fullname and current date.
This output can then be saved as structured CSV file
$targetFolder = "D:\"
$fileList = Get-Content -Path "C:\DeleteList.txt"
$deleted = Get-ChildItem -Path $targetFolder -Recurse -Include $fileList | ForEach-Object {
# output an object with the current date and the file FullName
$_ | Select-Object #{Name = 'DeletedOn'; Expression = {(Get-Date)}}, FullName
$_ | Remove-Item -WhatIf
}
# output on screen
$deleted | Format-Table -AutoSize
# output to csv file
$deleted | Export-Csv -Path 'C:\RemovedFiles.csv' -NoTypeInformation
Remove the -WhatIf safety-switch if you are satisfied with the results shown on screen.
Would this work?
$targetFolder = "D:"
$fileList = "C:\DeleteList.txt"
$Files = Get-ChildItem -Path "$targetFolder" -Recurse -Include #(Get-Content $fileList)
# Once you have the desires files stored in the $Files variable, then run a Foreach loop.
$Obj = #() # create an array called $Obj
Foreach ($File in $Files)
{
# store info in hash table
$hash = #{
DateTime = (get-date)
fileName = $File.name
fullpath = $File.fullname
}
Write-Host "deleting file $($file.name)" -for cyan
Remove-Item $File.fullname # *** BE VERY CAREFUL!!!***
# record information in an array called $Obj
$Obj += New-Object psobject -Property $hash
}
$Obj | select fileName, DateTime | Export-csv C:\...

Powershell - How To Filter Output

I need assistance in filtering what is going in to my output.
Here is my code:
$FolderPath = Get-ChildItem -Directory -Path "Z:\D482\F11\SECURE" -Recurse -Force
$Output = #()
ForEach ($Folder in $FolderPath) {
$Acl = Get-Acl -Path $Folder.FullName
ForEach ($Access in $Acl.Access) {
$Properties = [ordered]#{'Folder Name'=$Folder.FullName;'Group/User'=$Access.IdentityReference }
$Output += New-Object -TypeName PSObject -Property $Properties
}
}
$Output | ConvertTo-Csv | Out-File C:\Temp\Secured.txt
My output looks like this:
#TYPE System.Management.Automation.PSCustomObject
"Folder Name","Group/User"
"Z:\D482\F11\SECURE\QA\CDM\To_Load\Duals\Load_Completed","S-1-5-21-1275210071-879983540-1801674531-105509"
"Z:\D482\F11\SECURE\QA\Files\CDM\To_Load\Duals\Load_Completed","S-1-5-21-1275210071-879983540-1801674531-121910"
"Z\D482\F11\SECURE\QA\D482\To_Load\Duals\Load_Completed","DOMAIN\CXL3708"
In my output, I only want lines that contain our domain name ( as illustrated by the line with DOMAIN in it.
I have not been successful - either I get nothing, or I get error messages in the console.
here is one way to do the job ... [grin]
what it does ...
sets the constants
builds a parameter splat for the Get-ChildItem call
grabs the dirs from the source path
iterates thru the dir list
gets the acl list for the current dir & filters for those that start with the required domain name
note that i don't have a .IdentityReference property in my ACL, so i used .Owner instead.
iterates thru those acl items
builds a [PSCustomObject] for the current acl
sends that out to the $AccessInfo collection
displays the content of the above on screen
saves the collection to a csv file
the code ...
$SourcePath = $env:TEMP
$DomainName = $env:USERDOMAIN
$ReportFile = "SO_Erich_Powershell - How To Filter Output.csv"
$FullReportFile = Join-Path -Path $env:TEMP -ChildPath $ReportFile
$GCI_Params = #{
LiteralPath = $SourcePath
Directory = $True
Force = $True
Recurse = $True
ErrorAction = 'SilentlyContinue'
}
$DirList = Get-ChildItem #GCI_Params
$AccessInfo = foreach ($DL_Item in $DirList)
{
$AclList = Get-Acl -LiteralPath $DL_Item.FullName |
Where-Object {
$_.Owner -match "^$DomainName"
}
foreach ($AL_Item in $AclList)
{
[PSCustomObject]#{
DirName = $DL_Item.FullName
# my single system has no ".IdentityReference" property
# so i used ".Owner"
GroupOrUser = $AL_Item.Owner
}
}
}
# display the data
$AccessInfo
# send to a csv file
$AccessInfo |
Export-Csv -LiteralPath $FullReportFile -NoTypeInformation
truncated screen output ...
DirName GroupOrUser
------- -----------
C:\Temp\1 MySysName\AnotherUserName
C:\Temp\2 MySysName\AnotherUserName
C:\Temp\3 MySysName\AnotherUserName
[*snip ...*]
C:\Temp\vscode-update-system-x64 MySysName\MyUserName
C:\Temp\WPF MySysName\MyUserName
C:\Temp\mbam\qt-jl-icons MySysName\MyUserName
truncated csv file content ...
"DirName","GroupOrUser"
"C:\Temp\1","MySysName\AnotherUserName"
"C:\Temp\2","MySysName\AnotherUserName"
[*snip ...*]
"C:\Temp\WPF","MySysName\MyUserName"
"C:\Temp\mbam\qt-jl-icons","MySysName\MyUserName"

Creating a Powershell script to get network folder information Problem with capturing and displaying LastWriteTime

Okay i am not a programmer and my Powershell experience is basic. But here goes. I have been asked to collect some info on a Directory we are migrating off our network.
It collects sub dirs names, size, #of files and folders and datestamp and exports to csv.
I cannot for the life of me make the folder creation date work so i gave up on that and have been looking to get the lastwritetime for the folders as i am trying to figure out what has been used recently. It only works for a few folders but the rest in excel have system.object[] in the cell. Super frustrating.
Here is the code. It uses a gui directory picker.
#Refresh network drives for session
net use i: /delete
net use m: /delete
net use i: "\\wfs.queensu.ca\ADV\Workgroups"
net use m: "\\wfs.queensu.ca\ADVMedia"
Function Get-Folder($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")|Out-Null
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.Description = "Select a folder"
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
return $folder
}
$forDir = Get-Folder
#Change this to the parent directory that you want counts for
#$forDir = "\\wfs.queensu.ca\adv\workgroups\ADV Services\$seldir"
$Dirs = Get-ChildItem $forDir -Directory -Name
$Tab = [char]9
$results = #()
Write-Host $forDir
foreach($Dir in $Dirs)
{
$dirSize = "{0:N2} MB" -f ((Get-ChildItem $forDir/$Dir -Recurse | Measure-Object -Property Length
-Sum -ErrorAction Stop).Sum / 1MB)
$dirFiles = Get-ChildItem $forDir/$Dir -Recurse -File | Measure-Object | %{$_.Count}
$dirFolders = Get-ChildItem $forDir/$Dir -Recurse -Directory | Measure-Object | %{$_.Count}
#$dirDate = (Get-ChildItem $forDir/$Dir).LastWriteTime.ToString
$dirDate = #(Get-ChildItem $forDir/$Dir | % {$_.LastWriteTime})
$details = [ordered] #{
dir = $Dir
No_Files = $dirFiles
No_Folders = $dirFolders
size = $dirSize
date = $dirDate
}
$results += New-Object PSobject -Property $details
}
#This line finds the last index of the slash and adding one char
$Dirlength = $forDir.LastIndexOf('\') + 1
#This line takes the entire length of the string minus the postion above leaving the directory name
$sublength = $forDir.length - $Dirlength
#Assigns the remaining characters from the substring to the varibale to be used as the filename
$DirName = $forDir.SubString($Dirlength, $sublength)
$results | Export-Csv "C:\$DirName.csv" -NoTypeInformation
Write-Host ("Complete WOW!")
Get-ChildItem .\dir gives you all files contained in the directory .\dir not the directory itself.
That is why the following line in your script creates an array of LastWriteTimes for all files that are contained in the directory that $forDir/$Dir resolves to in your foreach loop:
$dirDate = #(Get-ChildItem $forDir/$Dir | % {$_.LastWriteTime})
The array in $dirDate will return ​System.Object[] when its toString() method is called. This is the reason, why you see this string in your excel, where you expect the folder's timestamp.
I bet that those folders, that seem to work do have exactly one childitem...
To get the LastWriteTime of the directory itself use Get-Item instead of Get-ChildItem.
$dirDate = Get-Item $forDir/$Dir | Select-Object -Expand LastWriteTime
try this...
Get-ChildItem -Path 'D:\temp' -Recurse |
Where-Object { $_.PSIsContainer } |
Select-Object -Property Name, LastWriteTime
<#
# Results
Name LastWriteTime
---- -------------
est 17-Feb-20 15:50:53
LogFiles 11-Mar-20 11:37:28
NewFolder 06-Feb-20 14:56:48
ParentFolder 12-Feb-20 14:24:25
Reference 03-Feb-20 11:55:47
Source 06-Feb-20 14:56:48
Target 24-Feb-20 22:03:56
New folder 03-Feb-20 11:55:24
temp 20-Jan-20 11:17:42
ChildFolder 12-Feb-20 14:08:11
GrandchildFolder 12-Feb-20 14:08:32
#>
# Or in v3 and beyond
Get-ChildItem -Path 'D:\temp' -Directory -Recurse |
Select-Object -Property Name, LastWriteTime
<#
# Results
Name LastWriteTime
---- -------------
est 17-Feb-20 15:50:53
LogFiles 11-Mar-20 11:37:28
NewFolder 06-Feb-20 14:56:48
ParentFolder 12-Feb-20 14:24:25
Reference 03-Feb-20 11:55:47
Source 06-Feb-20 14:56:48
Target 24-Feb-20 22:03:56
New folder 03-Feb-20 11:55:24
temp 20-Jan-20 11:17:42
ChildFolder 12-Feb-20 14:08:11
GrandchildFolder 12-Feb-20 14:08:32
#>
I know this question has already been answered, but for completeness, here's another way of doing this by utilizing the GetFileSystemInfos method every DirInfo object has.
$rootFolder = 'X:\YourRootPath'
Get-ChildItem -Path $rootFolder -Directory -Recurse | ForEach-Object {
# GetFileSystemInfos() (needs .NET 4+) is faster than Get-ChildItem and returns hidden objects by default
# See: https://devblogs.microsoft.com/powershell/why-is-get-childitem-so-slow/
$fsObjects = $_.GetFileSystemInfos('*', 'TopDirectoryOnly') # TopDirectoryOnly --> do not recurse
# you can also use Get-ChildItem here of course.
# To also get hidden files, with Get-ChildItem you need to add the -Force switch
# $fsObjects = Get-ChildItem -Path $_.FullName -Filter '*' -Force
# from the $fsObjects array, filter out the files and directories in order to get the count
$files = $fsObjects | Where-Object { $_ -is [System.IO.FileInfo] } # or: !($_.Attributes -band 'Directory')
$folders = $fsObjects | Where-Object { $_ -is [System.IO.DirectoryInfo] } # or: $_.Attributes -band 'Directory'
# emit a PSObject with all properties you wish to collect
[PsCustomObject]#{
Path = $_.FullName
FileCount = $files.Count
DirCount = $folders.Count
DirSize = "{0:N2} MB" -f (($files | Measure-Object -Sum -Property Length).Sum / 1MB)
DirDate = $_.LastWriteTime
}
} | Export-Csv -Path "X:\YourFolder_Info.csv" -NoTypeInformation -UseCulture

Powershell - Applying a looped function to subdirectories

I am new to Powershell and am struggling a bit. I have obtained an example of the sort of function I want to use and adapted it partially. What I want is for it to loop through each subdirectory of C:\Test\, and combine just the PDFs in each subdirectory together (leaving the resulting PDF in each subdirectory).
At the moment I can get it to comb through the subdirectories, but it then combines the contents of all subdirectories into one giant PDF in the top level directory, which is not what I want. I feel like maybe I need to use an array of sorts but I don't know Powershell well enough yet.
BTW this uses PDFSharp - a .Net library.
Function PDFCombine {
$filepath = 'C:\Test\'
$filename = '.\Combined' #<--- ???
$output = New-Object PdfSharp.Pdf.PdfDocument
$PdfReader = [PdfSharp.Pdf.IO.PdfReader]
$PdfDocumentOpenMode = [PdfSharp.Pdf.IO.PdfDocumentOpenMode]
foreach($i in (gci $filepath *.pdf -Recurse)) {
$input = New-Object PdfSharp.Pdf.PdfDocument
$input = $PdfReader::Open($i.fullname, $PdfDocumentOpenMode::Import)
$input.Pages | %{$output.AddPage($_)}
}
$output.Save($filename)
}
Your question was unclear about how many levels you need to go down. You can try this (untested). It goes one level down from $filepath, gets all pdf files in that folder and it's subfolders and combines them into Subfoldername-Combined.pdf:
Function PDFCombine {
$filepath = 'C:\Test\'
$PdfReader = [PdfSharp.Pdf.IO.PdfReader]
$PdfDocumentOpenMode = [PdfSharp.Pdf.IO.PdfDocumentOpenMode]
#Foreach subfolder(FIRST LEVEL ONLY!)
Get-ChildItem $filepath | Where-Object { $_.PSIsContainer } | Foreach-Object {
#Create new ouput pdf-file
$output = New-Object PdfSharp.Pdf.PdfDocument
$outfilepath = Join-Path $_.FullName "$($_.Name)-Combined.pdf"
#Find and add pdf files in subfolders
Get-ChildItem -Path $_.FullName -Filter *.pdf -Recurse | ForEach-Object {
#$input = New-Object PdfSharp.Pdf.PdfDocument #Don't think this one's necessary
$input = $PdfReader::Open($_.fullname, $PdfDocumentOpenMode::Import)
$input.Pages | %{ $output.AddPage($_) }
}
#Save
$output.Save($outfilepath)
}
}
So you should get this:
c:\Test\Folder1\Folder1-Combined.pdf #should include all pages in Folder1 and ANY subfolders below)
c:\Test\Folder2\Folder2-Combined.pdf #should include all pages in Folder2 and ANY subfolders below)
#etc.
If you need it to create a combined pdf for every subfolder(not only the first level), then you could try this(untested):
Function PDFCombine {
$filepath = 'C:\Test\'
$PdfReader = [PdfSharp.Pdf.IO.PdfReader]
$PdfDocumentOpenMode = [PdfSharp.Pdf.IO.PdfDocumentOpenMode]
#Foreach subfolder with pdf files
Get-ChildItem -Path $filepath -Filter *.pdf -Recurse | Group-Object DirectoryName | ForEach-Object {
#Create new ouput pdf-file
$output = New-Object PdfSharp.Pdf.PdfDocument
$outfilepath = Join-Path $_.Name "Combined.pdf"
#Find and add pdf files in subfolders
$_.Group | ForEach-Object {
#$input = New-Object PdfSharp.Pdf.PdfDocument #I don't think you need this
$input = $PdfReader::Open($_.fullname, $PdfDocumentOpenMode::Import)
$input.Pages | %{ $output.AddPage($_) }
}
#Save
$output.Save($outfilepath)
#Remove output-object
Remove-Variable output
}
}
not tested ...
Function PDFCombine {
$filepath = 'C:\Test\'
$filename = '.\Combined' #<--- ???
$output = New-Object PdfSharp.Pdf.PdfDocument
$PdfReader = [PdfSharp.Pdf.IO.PdfReader]
$PdfDocumentOpenMode = [PdfSharp.Pdf.IO.PdfDocumentOpenMode]
$lastdir=""
foreach($i in (gci $filepath *.pdf -Recurse)) {
$input = New-Object PdfSharp.Pdf.PdfDocument
$input = $PdfReader::Open($i.fullname, $PdfDocumentOpenMode::Import)
$input.Pages | %{$output.AddPage($_)}
if ($lastdir -ne $_.directoryname){
$lastdir=$_.directoryname
$output.Save("$lastdir\$filename")
$output = New-Object PdfSharp.Pdf.PdfDocument
}
}
}

File and folder count

There is a folder on the remote server which has various subfolders in it. It is completely nested. I would like to:
Prepare an HTML report which contains folder name.
For every folder it should also record the file count.
The code needs to append the HTML file which is already created.
Columns required: Folder name, Folder Path, File Count
Below is the code snippet which is part of my main script. I am fairly new to PowerShell.
Can some one please help?
$server_dir = "D:\Data\Inbox"
$does_dir_e = (Test-Path $server_dir)
if($does_dir_e)
{
$fso = New-Object -com "Scripting.FileSystemObject"
$f = $fso.GetFolder($server_dir)
foreach($folder in $f.subfolders)
{
$fcount = $((Get-ChildItem $folder.Path).count)
$fname = $folder.name | Convertto-HTML -Fragment >> C:\Temp\Server.html
}
}
You don't actually say what isn't working for you, but the following script should get you started.
The outer loop recurses through the folders (PSIsContainer) means it is a folder.
The inner loop counts the number of files in each folder using measure-object, we filter out folders from this count to give us just the file count.
$path = "D:\Data\Inbox"
# Enumerate the given path recursively
Get-ChildItem -Path $path -Recurse | Where-Object {$_.PSIsContainer} | %{
# Add a user-defined custom member with a value of the filecount this
# time not recursively (using measure object)
$_ | add-member -membertype noteproperty -name FileCount -value (Get-ChildItem -Path $_.Fullname |
Where-Object {!$_.PSIsContainer} |
Measure-Object).Count
# Output the required values
$_ | select Name, FullName, FileCount | ConvertTo-Html -Fragment
}
Is this what you want? I haven't used the HTML cmdlet before, so be aware it's ugly : )
$server_dir = 'D:\Data\Inbox'
if(Test-Path $server_dir)
{
$folders = Get-ChildItem $server_dir -Recurse | where {$_.PSIsContainer}
$output = #()
foreach($folder in $folders)
{
$fname = $folder.Name
$fpath = $folder.FullName
$fcount = Get-ChildItem $fpath | where {!$_.PSIsContainer} | Measure-Object | Select-Object -Expand Count
$obj = New-Object psobject -Property #{FolderName = $fname; FolderPath = $fpath; FileCount = $fcount}
$output += $obj
}
#Output to HTML
$output | ConvertTo-Html -Fragment >> 'C:\Temp\Server.html'
}