how to Exclude the list of selected files in powershell - powershell

In $FilesInContainer2 the exclude function is not working as expected.
I need the list of files from $files which is not in $FilesInContainer1
$srcdir = "W:\XXX\"
$zipFilepath = "W:\YYY"
$currentFileExtension = ".txt"
$logFileName = "W:\Log.txt"
$files = Get-ChildItem $srcdir -Filter *.txt | ? {$_.LastWriteTime -ge (Get-
Date).Date} | Sort-Object -Property Name -Descending
$Container1FileCount = [math]::Ceiling(($files).count/2)
$FilesInContainer1 = $files | Select-Object -First $Container1FileCount
$FilesInContainer2 = Get-ChildItem $files -Exclude $FilesInContainer1

Below code will write the list of files from $files which is not in $FilesInContainer1
$FilesInContainer2 = $files | ?{$FilesInContainer1 -notcontains $_}

Why would you do a Get-ChildItem twice at all?
To split the collection of files obtained with the first Get-ChildItem cmdlet, you can simply split that in two by doing something like this:
$srcdir = 'W:\XXX'
$files = Get-ChildItem -Path $srcdir -Filter '*.txt' -File |
Where-Object {$_.LastWriteTime -ge (Get-Date).Date} |
Sort-Object -Property Name -Descending
$half = [math]::Ceiling(($files.Count / 2))
# get the first half of the files colection
$FilesInContainer1 = $files | Select-Object -First $half
# or use $FilesInContainer1 = $files[(0..($half - 1))]
# get the rest (second half) of the files collection
$FilesInContainer2 = $files | Select-Object -Last ($files.Count - $half)
# or use $FilesInContainer2 = $files[($half..($files.Count - 1))]

Related

Powershell Find all empty folders and subfolders in a given Folder name

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.

Folders with more than 40.000 files

I have this script I received to check folders and subfolders on a network drive. I wonder how it could be modified into checking only folders and subfolder and write in the CSV if there is any folder with more then 40.000 files in it and the number of files. The image show a sample output from the script as it is now and I do not need it to show any files as it currently do.
$dir = "D:\test"
$results = #()
gci $dir -Recurse -Depth 1 | % {
$temp = [ordered]#{
NAME = $_
SIZE = "{0:N2} MB" -f ((gci $_.Fullname -Recurse | measure -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB)
FILE_COUNT = (gci -File $_.FullName -Recurse | measure | select -ExpandProperty Count)
FOLDER_COUNT = (gci -Directory $_.FullName -Recurse | measure | select -ExpandProperty Count)
DIRECTORY_PATH = $_.Fullname
}
$results += New-Object PSObject -Property $temp
}
$results | export-csv -Path "C:\temp\output.csv" -NoTypeInformation
Instead of executing so many Get-ChildItem cmdlets, here's an approach that uses robocopy to do the heavy lifting of counting the number of files, folders and total sizes:
# set the rootfolder to search
$dir = 'D:\test'
# switches for robocopy
$roboSwitches = '/L','/E','/NJH','/BYTES','/NC','/NP','/NFL','/XJ','/R:0','/W:0','/MT:16'
# regex to parse the output from robocopy
$regEx = '\s*Total\s*Copied\s*Skipped\s*Mismatch\s*FAILED\s*Extras' +
'\s*Dirs\s*:\s*(?<DirCount>\d+)(?:\s+\d+){3}\s+(?<DirFailed>\d+)\s+\d+' +
'\s*Files\s*:\s*(?<FileCount>\d+)(?:\s+\d+){3}\s+(?<FileFailed>\d+)\s+\d+' +
'\s*Bytes\s*:\s*(?<ByteCount>\d+)(?:\s+\d+){3}\s+(?<BytesFailed>\d+)\s+\d+'
# loop through the directories directly under $dir
$result = Get-ChildItem -Path $dir -Directory | ForEach-Object {
$path = $_.FullName # or if you like $_.Name
$summary = (robocopy.exe $_.FullName NULL $roboSwitches | Select-Object -Last 8) -join [Environment]::NewLine
if ($summary -match $regEx) {
$numFiles = [int64] $Matches['FileCount']
if ($numFiles -gt 40000) {
[PsCustomObject]#{
PATH = $path
SIZE = [int64] $Matches['ByteCount']
FILE_COUNT = [int64] $Matches['FileCount']
FOLDER_COUNT = [int64] $Matches['DirCount']
}
}
}
else {
Write-Warning -Message "Path '$path' output from robocopy was in an unexpected format."
}
}
# output on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path "C:\temp\output.csv" -NoTypeInformation

size of files based on particular extensions

I am trying to find all files in a specific drive that are accessed before 40 days, find their file type and specificy size of files based on extension.
I have 40000 files in 126 folders, and total of 51 different extensions (file types), now that i want to find what type of file occupy who much space.
With this I got file count based on extension:
Get-ChildItem -Path X:\ -Recurse |where{-not $_.PSIsContainer} | group Extension -NoElement
With this I got number of unique extensions:
Get-ChildItem -Path X:\ -Recurse | Select-Object -Property Extension -Unique
Get-ChildItem -Path X:\ -Recurse |where{-not $_.PSIsContainer} | group Extension -NoElement
Get-ChildItem -Path X:\ -Recurse | Select-Object -Property Extension -Unique
Maybe try this.
# directory path
$directory = "C:\Program Files"
# date -40 days
$date = (Get-Date).AddDays(-40)
# files which are accessed before 40 days
$files = Get-ChildItem $directory -Recurse -File:$true | Where-Object {$_.LastWriteTime -gt $date}
# sort files by extension, size
$files | Sort-Object Extension,Length | Select-Object Name,#{label = 'Length'; e = {"{0:N3} KB" -f ($_.Length / 1KB)}} | ft -AutoSize -Wrap
I'll hope it is not so bad. :)
# directory path
$directory = "X:\"
# date -40 days
$date = (Get-Date).AddDays(-40)
# files which are accessed before 40 days
$files = Get-ChildItem $directory -Recurse -File:$true | Where-Object {$_.LastWriteTime -gt $date}
# file extensions
$fileExtensions = $files | Select-Object -Property Extension -Unique
foreach ($ext in $fileExtensions){
[int]$fileSize = $null
foreach ($file in $files) {
if ($file.Extension -like $ext.Extension) {
[int]$fileSize += $file.Length / 1KB
}
}
Write-Host "Extension:" $ext.Extension " - Size: "$fileSize "KB"
}
Maybe something like this, I'm not sure how to format it in a table:
$files = Get-ChildItem $directory -Recurse -File:$true | Where-Object {$_.LastWriteTime -gt $date}
foreach($file in $files){
[IO.Path]::GetExtension($file)
$file.length
}

How to filter subdirectories based off the item count in Powershell

I am trying to filter folders based off of the count for the number of files in each folder.
I have been able to list the folder name and the value if there is a value greater than 1. I am trying to exclude the folders that may contain no items.
The amount of items changes daily.
$Date2 = Get-Date -Format "yyyy-MM-dd"
$Date2Str = '{0:yyyy-MM-dd}' -f $Date2
$startFolder = "U:\test"
#Returns the Count of files in each queue
$colItems = (Get-ChildItem $startFolder -recurse | Where-Object
{$_.PSIsContainer -eq $True} | Sort-Object)
if($colItems -ine $null){
foreach ($i in $colItems)
{
$subFolderItems = (Get-ChildItem $i.FullName | Where-Object
($_.CreationTime -lt $Date2Str -and $_.Name -like "*.tif"))
$i.Name + " -- " -f ($subFolderItems.Count) |Format-Table
#{Expression={$colItems -ge 1}}
I expect the output of $colItems to be the subFolder name, and the count, excluding any subFolder with a Count less than 1 or equals to 0.
The actual return is the list of subfolder Names and counts with all subFolders including those with a count equal to 0.
If I've got your explanation right you're looking for something like this:
$startFolder = 'U:\test'
Get-ChildItem -Path $startFolder -Directory |
Select-Object -Property Name, #{Name = 'FileCount'; Expression = { (Get-ChildItem -Path $_.FullName -File).count}}
That lists all subfolders of your $startFolder together with their file count.
BTW: This code expects at least Powershell version 3.
... and of course you can pipe this now to a Where-Object and output only the folders with more than one file in it ...
Get-ChildItem -Path $startFolder -Directory |
Select-Object -Property Name, #{Name = 'FileCount'; Expression = { (Get-ChildItem -Path $_.FullName -File).count } } |
Where-Object -Property FileCount -GT -Value 1

powershell get-childitem to csv with details

I am trying to create a CSV file for a file share that has deep folder structure.
I want the CSV to look like:
filename, filepath, file type, folderStructure
So far I have the following:
#Get-ChildItem -Path D:\ -Recurse
$directory="d:\"
gci $directory -recurse |
where {$_.psiscontainer} |
foreach {
get-childitem $_.fullname |
sort creationtime |
select -expand fullname -last 1
}
You don't need to recurse a recursive in Powershell. It will automatically go through all of the subdirectories of subdirectories.
I am also a little unsure of some of the information you wanted, but here is a script that does what you want mostly I believe and is IMO a little better to read.
Get-ChildItem -Path X:\Test -Recurse |`
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="filePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}}`
}| Export-Csv X:\Test\Results.csv -NoTypeInformation
You will need to change your path, as I created this for a test. My results look like this in Excel:
+-------------------------------+----------------+-------------------------------------+-----------+
| Name | Created | filePath | Extension |
+-------------------------------+----------------+-------------------------------------+-----------+
| test2 | 3/6/2013 19:21 | X:\Test\test2 | Folder |
| Results.csv | 3/6/2013 19:51 | X:\Test\Results.csv | .csv |
| test3 | 3/6/2013 19:21 | X:\Test\test2\test3 | Folder |
| New Text Document.txt | 3/6/2013 19:21 | X:\Test\test2\New Text Document.txt | .txt |
+-------------------------------+----------------+-------------------------------------+-----------+
Where it says "Folder" for the Extension just it returning that it is a directory instead of a blank (No extension).
OK, I changed the way it checks for the parent. It is no longer looking directly at the Parent attribute, and it should correct it now.
Get-ChildItem -Path D:\ -Recurse |`
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$ParentS = ($_.Fullname).split("\")
$Parent = $ParentS[#($ParentS.Length - 2)]
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="Folder Name";e={if($Parent){$Parent}else{$Parent}}},`
#{n="filePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}}`
}| Export-Csv X:\Test\ResultsC.csv -NoTypeInformation
It is now taking the path to the current item, turning it into an array by splitting on the \, and then giving you the value at ArryLength - 2
I am not sure if this is the best approach but it works. I have a feeling code could be shorten to get the full path of a file.
$myDirectory = "D:\"
Get-ChildItem -Path $myDirectory -Recurse |`
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$ParentS = ($_.Fullname).split("\")
$Parent = $ParentS[#($ParentS.Length - 2)]
$ParentPath = $_.PSParentPath
$ParentPathSplit = ($_.PSParentPath).split("::")
$ParentPathFinal = $ParentPathSplit[#($ParentPathSplit.Length -1)]
#$ParentPath = [io.path]::GetDirectoryName($myDirectory)
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="Folder Name";e={if($Parent){$Parent}else{$Parent}}},`
#{n="filePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}},`
#{n="Folder Name 2";e={if($Parent){$Parent}else{$Parent}}},`
##{n="Folder Path";e={$ParentPath}},`
#{n="Folder Path 2";e={$ParentPathFinal}}`
}| Export-Csv d:\ResultsC_2_F.csv -NoTypeInformation