when i create an Out-File i open the .txt file the filenames are not in order please see code i am using and see the Out-File
i am using this to add the Number at the beginning.
$button20_Click={
$originalFiles = Get-ChildItem $textbox3.Text -Filter *.pdf
$x = 1
ForEach ($originalFile in $originalFiles) {
Rename-Item -Path $originalFile.FullName -NewName (($originalFile.Directory.FullName) + "\" + $x + $originalFile.Name.substring(8))
$x++
}
}
$button6_Click = {
$listBox2.items.clear()
$CreateRun = get-childitem $textbox3.text *.pdf
$outTXT = $textbox8.Text
"`n",$textbox12.text ,"`n", $CreateRun.Name| Out-File $outTXT\$($comboBox1.SelectedItem + "-" + $comboBox2.SelectedItem + "-"+ $dateTimePicker1.Text)".txt"
$showFile = GCI $textbox3.Text -Filter *.pdf
Foreach($show in $showFile){
$listbox2.Items.Add($show.BaseName)
}
}
To prefix file names with a (textual) sortable index number, you best use leading zeros for numbers, so 1.filename becomes 001.filename it the total number of files in the folder has that many files.
Something like this should do that:
$originalFiles = Get-ChildItem -Path $textbox3.Text -Filter *.pdf -File
# calculate the number of digits needed to prefix with leading zeros
$numDigits = $originalFiles.Count.ToString().Length
$startIndex = 1
$originalFiles | ForEach-Object {
$_ | Rename-Item -NewName ("{0:D$numDigits}.{1}" -f $startIndex++, $_.Name)
}
Original
D:\TEST
test-INV67459-TW15 10DG.pdf
test-INV67459-TW15 11DG.pdf
test-INV67459-TW15 12DG.pdf
test-INV67459-TW15 13DG.pdf
test-INV67459-TW15 14DG.pdf
test-INV67459-TW15 3DG.pdf
test-INV67459-TW15 4DG.pdf
test-INV67459-TW15 5DG.pdf
test-INV67459-TW15 6DG.pdf
test-INV67459-TW15 7DG.pdf
test-INV67459-TW15 8DG.pdf
test-INV67459-TW15 9DG.pdf
Becomes
D:\TEST
01.test-INV67459-TW15 10DG.pdf
02.test-INV67459-TW15 11DG.pdf
03.test-INV67459-TW15 12DG.pdf
04.test-INV67459-TW15 13DG.pdf
05.test-INV67459-TW15 14DG.pdf
06.test-INV67459-TW15 3DG.pdf
07.test-INV67459-TW15 4DG.pdf
08.test-INV67459-TW15 5DG.pdf
09.test-INV67459-TW15 6DG.pdf
10.test-INV67459-TW15 7DG.pdf
11.test-INV67459-TW15 8DG.pdf
12.test-INV67459-TW15 9DG.pdf
Related
I'm basically trying to build a routine that reads a named folder directory, builds a CSV file, then reads in that CSV file, manipulates some of the properties to split data into new columns, exports that to another CSV.
This I have achieved with the following code:
$Folder = Read-Host 'Please enter a folder path'
$File = Read-Host 'Please enter a filename'
$OutputFile = $Folder + '\' + $File + '.csv'
$SplitFile = $Folder + '\' + $File + '_Split.csv'
$CopyDir = $Folder + '\WantedDocs\'
Get-ChildItem $Folder -Recurse -Include *.* |
select Directory, FullName, Name |
Export-Csv -Delimiter ',' -NoTypeInformation $OutputFile
$a = Import-Csv $OutputFile
$values = $a.Name.Split("_")
$a | Add-Member 'CliCode' -NotePropertyValue $values[3]
$a | Add-Member 'CopyDir' -NotePropertyValue $CopyDir
$a | Select Directory, FullName, Name, CliCode, CopyDir |
Export-Csv -Delimiter ',' -NoTypeInformation $SplitFile
Excuse me if my terminology isn't right, but I am now looking to build a batch file full of xcopy commands, using item values from the properties.
xcopy 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt' 'C:\Test\WantedDocs\*' /Y
When you use Import-Csv and assign it to a variable, that variable contain various properties, with each property containing an array of values taken from each line in the CSV file.
In my example the variable $a has properties called "Directory", "FullName" and "Name", the headers of the 3 columns in my CSV file.
If my CSV file contains these lines:
"Directory","FullName","Name"
"C:\Test\OriginalDocs\A","C:\Test\OriginalDocs\A\file1_A_A_12345678.txt","file1_A_A_12345678.txt"
"C:\Test\OriginalDocs\B","C:\Test\OriginalDocs\B\file2_B_B_43534554.txt","file1_B_B_43534554.txt"
The Directory property would be an array of 2 items: "C:\Test\OriginalDocs\A" and "C:\Test\OriginalDocs\B\"
The FullName property would be an array of 2 items: "C:\Test\OriginalDocs\A\file1_A_A_12345678.txt" and "C:\Test\OriginalDocs\B\file2_B_B_43534554.txt"
The Name property would be an array of 2 items: "file1_A_A_12345678.txt" and "file2_B_B_43534554.txt"
What I want to know is how would I be able to select all [0] items in the array for each property and build the xcopy command
e.g. if I do this:
$xc1 = "xcopy '"
$xc2 = $a.FullName
$xc3 = "' '"
$xc4 = $a.CopyDir
$xc5 = $a.CliCode
$xc6 = "\*' /Y"
$xcopy = $xc1 + $xc2 + $xc3 + $xc4 + $xc5+ $xc6
The resulting $xcopy variable contains all array vales
e.g. for the example above the xcopy variable ends up with the value:
xcopy 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt C:\Test\OriginalDocs\B\file2_B_B_43534554.txt' 'C:\Test\OriginalDocs\WantedDocs\ C:\Test\OriginalDocs\WantedDocs\12345678 43534554\*' /Y
What I want to achieve is to effectively do this with the [0] array values from each selected property:
$xc1 = "xcopy '"
$xc2 = $a.FullName[0]
$xc3 = "' '"
$xc4 = $a.CopyDir[0]
$xc5 = $a.CliCode[0]
$xc6 = "\*' /Y"
$xcopy = $xc1 + $xc2 + $xc3 + $xc4 + $xc5+ $xc6
Write the $xcopy variable to the text file (using Add-Content I believe)
Then do the same with the [1] array values:
$xc1 = "xcopy '"
$xc2 = $a.FullName[1]
$xc3 = "' '"
$xc4 = $a.CopyDir[1]
$xc5 = $a.CliCode[1]
$xc6 = "\*' /Y"
$xcopy = $xc1 + $xc2 + $xc3 + $xc4 + $xc5+ $xc6
And so on until all items in the arrays are dealt with.
So producing a text/batch file with a line for each item in the arrays i.e. all the [0], all the [1] etc.
Using my example above I'd get a text file like below.
xcopy 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt' 'C:\Test\OriginalDocs\WantedDocs\12345678\*' /Y
xcopy 'C:\Test\OriginalDocs\B\file2_B_B_43534554.txt' 'C:\Test\OriginalDocs\WantedDocs\43534554\*' /Y
I've been looking at foreach and ForEach-Object but so far I've not found anything that works for my needs. Maybe it can't be done?
To work line by line use foreach:
foreach ($Line in $a){ DoSomethingLikeCopy $Line.FullName to "$CopyDir\$($Line.CliCode)" }
Instead of XCopy use New-Item to create a new textfile with the Value of the old file or to create the folder for the new Item:
Get-Content -Path 'C:\Test\OriginalDocs\A\file1_A_A_12345678.txt' -raw | New-Item -Path 'C:\Test\OriginalDocs\WantedDocs\12345678\file1_A_A_12345678.txt' -Force
or
New-Item -Path'C:\Test\OriginalDocs\WantedDocs\12345678\*' -ItemType directory
It's pointless to export data to a CSV that you're reading back right away. Simply use a pipeline. Also, xcopy is an external command and can be run directly from PowerShell, so there's no need to have PowerShell create a batch file first.
This should be all you need:
$Folder = Read-Host 'Please enter a folder path'
Get-ChildItem $Folder -Recurse | ForEach-Object {
$clicode = $_.BaseName.Split("_")[-1]
& xcopy $_.FullName "${Folder}\WantedDocs\${clicode}\*" /y
}
If you indeed want output CSV files for every step of the way, you can do something like this:
# YOU NEED TO ADD CODE FOR CHECKING THE USER INPUT
# What I'm doing here is very rudimentary..
do {
$Folder = Read-Host 'Please enter a folder path'
} while (-not (Test-Path -Path $Folder -PathType Container))
$File = Read-Host 'Please enter a filename (no extension)'
# at the very least sanitize the given filename, and get only the Name without Extension
$BaseName = [System.IO.Path]::GetFileNameWithoutExtension($File)
$OutputFile = Join-Path -Path $Folder -ChildPath ($BaseName + '.csv')
$SplitFile = Join-Path -Path $Folder -ChildPath ($BaseName + '_Split.csv')
$CopyDir = Join-Path -Path $Folder -ChildPath 'WantedDocs'
# collect the files and get the properties Directory, FullName and Name
$a = Get-ChildItem $Folder -Recurse -Include *.* -File | Select-Object Directory,FullName,Name
# write the first CSV file:
$a | Export-Csv -Path $OutputFile -Delimiter ',' -NoTypeInformation
# redefine the collection to add extra properties CliCode, CopyDir and Result
$a = $a | Select-Object *,CliCode,CopyDir,Result
# loop through the collection
$a | ForEach-Object {
# the automatic variable $_ is a single object in the collection
# get the CliCode from the Name property:
# if the filename is "file1_A_A_12345678.txt", the CliCode will be "12345678"
if ($_.Name -match '([^_.]+)\..*$') {
$cliCode = $matches[1]
$targetDir = Join-Path -Path $CopyDir -ChildPath $cliCode
$_.CliCode = $cliCode # example: "12345678"
$_.CopyDir = $targetDir # example: "C:\Test\WantedDocs\12345678"
# copy the file, but create the target folder first if this does not exist
if (-not (Test-Path -Path $targetDir -PathType Container)) {
New-Item -Path $targetDir -ItemType Directory | Out-Null
}
Copy-Item -Path $_.FullName -Destination $targetDir
$_.Result = "OK"
}
else {
# show the error and add "Failure" to the result property
Write-Warning "Skipped file '$_.FullName'. Reason: CliCode not found"
$_.Result = "Failure"
}
}
# output the results of the copy as CSV file
$a | Export-Csv -Path $SplitFile -Delimiter ',' -NoTypeInformation
When done, the files are copied to the new locations and you'll have two CSV files:
The first 'Something.csv' before the copy:
"Directory","FullName","Name"
"D:\Test\OriginalDocs\A","D:\Test\OriginalDocs\A\file1_A_A_12345678.txt","file1_A_A_12345678.txt"
"D:\Test\OriginalDocs\B","D:\Test\OriginalDocs\B\file2_B_B_43534554.txt","file2_B_B_43534554.txt"
and the second 'Something_Split.csv' after the copy:
"Directory","FullName","Name","CliCode","CopyDir","Result"
"D:\Test\OriginalDocs\A","D:\Test\OriginalDocs\A\file1_A_A_12345678.txt","file1_A_A_12345678.txt","12345678","D:\Test\OriginalDocs\WantedDocs\12345678","OK"
"D:\Test\OriginalDocs\B","D:\Test\OriginalDocs\B\file2_B_B_43534554.txt","file2_B_B_43534554.txt","43534554","D:\Test\OriginalDocs\WantedDocs\43534554","OK"
The 'Result' column will display Failure if the filename did not contain a CliCode in the name, otherwise OK
Thank you for all the replies. Using a combination of what has been advised, I now have the solution I need.
Many thanks for all the assistance. I've added an if else section to the file processing because I would only be interested in files that follow a specific naming convention (xx_xx_xx_clicode_xxx.ext). This is for a specific project where I'll be supplied with 1000s of files, most of which should follow the naming convention. So I'm checking the number of elements in the $values variable array to make sure it has at least 4 values (i.e. [3] exists as a value). Where it doesn't exist I'm writing the filename out to a log file.
This is the completed solution:
do {
$Folder = Read-Host 'Please enter a folder path'
} while (-not (Test-Path -Path $Folder -PathType Container))
$File = Read-Host 'Please enter a filename (no extension)'
$OutputFile = Join-Path -Path $Folder -ChildPath ($File + '.csv')
$SplitFile = Join-Path -Path $Folder -ChildPath ($File + '_Split.csv')
$CopyDir = Join-Path $Folder -ChildPath 'WantedDocs'
$logfile = "log.txt"
$log = Join-Path -Path $Folder -ChildPath $logfile
Get-ChildItem $Folder -Recurse -Include *.* | select Directory,FullName,Name | Export-Csv -Delimiter ',' -NoTypeInformation $OutputFile
$a = Import-Csv $OutputFile
$a | Add-Member 'CopyDir' -NotePropertyValue $CopyDir
$a | Select Directory,FullName,Name,CopyDir | Export-Csv -Delimiter ',' -NoTypeInformation $SplitFile
Foreach ($Row in $a)
{
$values = $Row.Name.split("_")
If ($values.Count -gt 3)
{
$tempfile = Join-Path -Path $CopyDir -ChildPath $values[3]
$OriginalFile = $($Row.FullName)
$CopyFile = $tempfile
New-Item -ItemType directory -Path $tempfile -Force
Copy-Item $OriginalFile -Destination $CopyFile -Force -Recurse
}
Else
{
Add-Content $log $Row.Name
}
}
Write-Output "Finished"
Many thanks once again. Much appreciated.
I have the following script which runs on .zip files in a directory which have a whole directory structure with many files. These files then have 7zip run on them to extract them and then .eml is added to the extracted file.
& "c:\program files\7-zip\7z.exe" x c:\TestZip -r -oC:\TestRestore 2> c:\TestLog\ziplog.txt
& "c:\program files\7-zip\7z.exe" x c:\TestRestore -r -aos -oc:\TestExtract 2> c:\TestLog\sevenzip.txt
gci -path "c:\TestExtract" -file | rename-item -newname {$PSItem.name + ".eml"}
My problem is that out of these files sometimes the final extraction cannot be done by 7zip as it does not see it as an archive. I have found that these particular files if I just put .eml on them they are accessible as emails. So when the archive fails to extract I write the output to the sevenzip.txt file.
What I need help with is how do I read this file to get the filenames and place them in a directory so I can add the .eml extension.
An example of the output in the sevenzip.txt file is as follows
ERROR: c:\TestRestore\0\0\54\3925ccb78f80d28b7569b6759554d.0_4011
Can not open the file as archive
ERROR: c:\TestRestore\0\0\5b\6fa7acb219dec5d9e55d4eadd6eb1.0_3958
Can not open the file as archive
Any help would be greatly appreciated on how to do this.
Sorry for all the comments but I am working on this
$SourceFile = 'c:\testlog\sevenzip.txt'
$DestinationFile = 'c:\testlog\testlogextractnew.txt'
$Pattern = 'c:\\TestRestore\\' (Get-Content $SourceFile) |
% {if ($_ -match $Pattern){$_}} |
Set-Content $DestinationFile (Get-Content $DestinationFile).replace('ERROR: ', '') |
Set-Content $DestinationFile (Get-Content$DestinationFile).replace('7z.exe : ', '') |
Set-Content $DestinationFile
If there are no other errors, then you can pick those filenames out of the file with a regex pattern match:
$filesToFix = Select-String -Pattern "ERROR: (.*)" -LiteralPath 'c:\testlog\sevenzip.txt' |
ForEach-Object {$_.Matches.Groups[1].Value}
And probably rename them with
$filesToFix | ForEach-Object {
Rename-Item -LiteralPath $_ -NewName {"$_.eml"}
}
If there might be other errors and you only want these, you'll need more work on the matching:
$fileContent = Get-Content -LiteralPath 'c:\testlog\sevenzip.txt' -Raw
$filesToFix = [regex]::Matches($fileContent,
"ERROR: (.*)\nCan not open the file as archive",
'Multiline') |
ForEach-Object {$_.Groups[1].Value}
This is what I ended going with it is in the comments because I didn't know how to format but have worked it out now so hopefully this makes it easier to understand in case it is useful.
This section creates a list of all the files that failed to extract with their directory location. For use in next section.
$SourceFile = 'c:\testlog\sevenzip.txt' $DestinationFile = c:\testlog\testlogextractnew.txt'
$Pattern = 'c:\\TestRestore\\'
(Get-Content $SourceFile) | % {if ($_ -match $Pattern){$_}} | Set-Content $DestinationFile
(Get-Content $DestinationFile).replace('ERROR: ', '') | Set-Content $DestinationFile
(Get-Content $DestinationFile).replace('7z.exe : ', '') | Set-Content $DestinationFile
This is just some result checking
Get-Content C:\testlog\testlogextractnew.txt | Measure-Object –Line > c:\testlog\numberoffilesthatfailed.txt
This copies all files listed in the txt file to another directory
c:\testlog\testlogextractnew.txt | copy-item -destination "c:\TestCopy"
This renames the current extensions to .eml
gci -path "C:\TestCopy" -file | rename-item -newname { [io.path]::changeextension($_.name, "eml")}
I have a directory of subfolders. Each one contains text files within it. I am trying to combine the files found in each subfolder.
Example:
SubFolder 1 → a.txt + b.txt + c.txt → SubFolder1Merged.txt
SubFolder 2 → x.txt + y.txt + z.txt → SubFolder2Merged.txt
I have referenced this thread.
This is what I have so far:
$startingDir = "C:\Users\WP\Desktop\TextFiles"
function CombineLogs {
param([string]$startingDir)
dir $startingDir -Filter *.txt | Get-Content |
Out-File (Join-Path $startingDir COMBINED.txt)
dir $startingDir | ?{ $_.PsIsContainer } | %{ CombineLogs $_.FullName }
}
CombineLogs 'C:\Users\WP\Desktop\CombinedTextFiles' #output the combined text files here
I get a combined.txt generated in CombinedTextFiles - but not individual files merged.
Also the file is empty.
I simply want to loop through each subfolder, merge the text files within each folder, then output to my CombinedTextfiles Folder.
function CombineLogs
{
param([string] $startingDir)
$outputFile = (Split-Path $startingDir -Leaf) + "COMBINED.txt"
dir $startingDir -Filter *.txt |
Get-Content |
Out-File (Join-Path $outputDir $outputFile)
dir $startingDir |?{ $_.PsIsContainer } | %{ CombineLogs $_.FullName }
}
$outputDir ='C:\Users\WP\Desktop\CombinedTextFiles' # output the combined text files here
CombineLogs "C:\Users\WP\Desktop\TextFiles"
Above code snippet would solve TextFilesCOMBINED.txt and NewCOMBINED.txt however does not solve ABCCOMBINED.txt nor xyzCOMBINED.txt in next scenario:
C:\Users\WP\Desktop\TextFiles\ABC\ABC
C:\Users\WP\Desktop\TextFiles\ABC\xyz\ABC
C:\Users\WP\Desktop\TextFiles\New
C:\Users\WP\Desktop\TextFiles\xyz\ABC
Recursion can be tricky if you don't know how to handle it. And in this case you don't need to implement recursion yourself anyway. Just let PowerShell do the heavy lifting for you:
$startingDir = 'C:\Users\WP\Desktop\TextFiles'
$combinedDir = 'C:\Users\WP\Desktop\CombinedTextFiles'
Get-ChildItem $startingDir -Recurse | Where-Object {
$txtfiles = Join-Path $_.FullName '*.txt'
$_.PSIsContainer -and (Test-Path $txtfiles)
} | ForEach-Object {
$merged = Join-Path $combinedDir ($_.Name + '_Merged.txt')
Get-Content $txtfiles | Set-Content $merged
}
I have this bit of powershell script but i can't get the $DirectoryName to behave as expected.
1,2,3 |
foreach {
$count = $_;
$x = gci -Path \\myserver-web$count\d$\IISLogs\ -include *.log -recurse
$x | Copy-Item -Destination D:\ServerLogsAndBackups\IIS\w$count\$_.DirectoryName_$_.Name -whatIf
}
When I run this though I get
What if: Performing operation "Copy File" on Target "Item: \myserver-web1\d$\IISLogs\W3SVC1165836668\ex101224.log Destination: D:\ServerLogsAndBackups\IIS\w1\1.DirectoryName_1.Name".
What I want it to be is
W3SVC1165836668_ex101206.log
where my directory structure is like:
\\myserver-web1\d$\IISLogs\W3SVC1165836668
\\myserver-web1\d$\IISLogs\W3SVC1165837451
\\myserver-web1\d$\IISLogs\W3SVC1165836966
\\myserver-web1\d$\IISLogs\W3SVC1165812365
with files called ex101206.log in each folder
Cheers
You need to evaluate the $_.Directoryname_$_.Name part. Like so,
$x | Copy-Item -Destination $(D:\ServerLogsAndBackups\IIS\w$count\$_.DirectoryName_$_.Name) -whatIf
1,2,3 |
foreach {
$count = $_;
gci -Path \\myserver-web$count\d$\IISLogs\ -include *.log -recurse | % { $dirName = $_.directoryname.Substring($_.directoryname.LastIndexOf("\")+1); $logname = $_.Name; $_ | Copy-Item -Destination $("D:\ServerLogsAndBackups\IIS\w"+$count+"\"+$dirname+"_"+$logName) -whatif }
}
I have a share that is a "junk drawer" for end-users. They are able to create folders and subfolders as they see fit. I need to implement a script to delete files created more than 31 days old.
I have that started with Powershell. I need to follow up the file deletion script by deleting subfolders that are now empty. Because of the nesting of subfolders, I need to avoid deleting a subfolder that is empty of files, but has a subfolder below it that contains a file.
For example:
FILE3a is 10 days old. FILE3 is 45 days old.
I want to clean up the structure removing files older than 30 days, and delete empty subfolders.
C:\Junk\subfolder1a\subfolder2a\FILE3a
C:\Junk\subfolder1a\subfolder2a\subfolder3a
C:\Junk\subfolder1a\subfolder2B\FILE3b
Desired result:
Delete: FILE3b, subfolder2B & subfolder3a.
Leave: subfolder1a, subfolder2a, and FILE3a.
I can recursively clean up the files. How do I clean up the subfolders without deleting subfolder1a? (The "Junk" folder will always remain.)
I would do this in two passes - deleting the old files first and then the empty dirs:
Get-ChildItem -recurse | Where {!$_.PSIsContainer -and `
$_.LastWriteTime -lt (get-date).AddDays(-31)} | Remove-Item -whatif
Get-ChildItem -recurse | Where {$_.PSIsContainer -and `
#(Get-ChildItem -Lit $_.Fullname -r | Where {!$_.PSIsContainer}).Length -eq 0} |
Remove-Item -recurse -whatif
This type of operation demos the power of nested pipelines in PowerShell which the second set of commands demonstrates. It uses a nested pipeline to recursively determine if any directory has zero files under it.
In the spirit of the first answer, here is the shortest way to delete the empty directories:
ls -recurse | where {!#(ls -force $_.fullname)} | rm -whatif
The -force flag is needed for the cases when the directories have hidden folders, like .svn
This will sort subdirectories before parent directories working around the empty nested directory problem.
dir -Directory -Recurse |
%{ $_.FullName} |
sort -Descending |
where { !#(ls -force $_) } |
rm -WhatIf
Adding on to the last one:
while (Get-ChildItem $StartingPoint -recurse | where {!#(Get-ChildItem -force $_.fullname)} | Test-Path) {
Get-ChildItem $StartingPoint -recurse | where {!#(Get-ChildItem -force $_.fullname)} | Remove-Item
}
This will make it complete where it will continue searching to remove any empty folders under the $StartingPoint
I needed some enterprise-friendly features. Here is my take.
I started with code from other answers, then added a JSON file with original folder list (including file count per folder). Removed the empty directories and log those.
https://gist.github.com/yzorg/e92c5eb60e97b1d6381b
param (
[switch]$Clear
)
# if you want to reload a previous file list
#$stat = ConvertFrom-Json (gc dir-cleanup-filecount-by-directory.json -join "`n")
if ($Clear) {
$stat = #()
} elseif ($stat.Count -ne 0 -and (-not "$($stat[0].DirPath)".StartsWith($PWD.ProviderPath))) {
Write-Warning "Path changed, clearing cached file list."
Read-Host -Prompt 'Press -Enter-'
$stat = #()
}
$lineCount = 0
if ($stat.Count -eq 0) {
$stat = gci -Recurse -Directory | %{ # -Exclude 'Visual Studio 2013' # test in 'Documents' folder
if (++$lineCount % 100 -eq 0) { Write-Warning "file count $lineCount" }
New-Object psobject -Property #{
DirPath=$_.FullName;
DirPathLength=$_.FullName.Length;
FileCount=($_ | gci -Force -File).Count;
DirCount=($_ | gci -Force -Directory).Count
}
}
$stat | ConvertTo-Json | Out-File dir-cleanup-filecount-by-directory.json -Verbose
}
$delelteListTxt = 'dir-cleanup-emptydirs-{0}-{1}.txt' -f ((date -f s) -replace '[-:]','' -replace 'T','_'),$env:USERNAME
$stat |
? FileCount -eq 0 |
sort -property #{Expression="DirPathLength";Descending=$true}, #{Expression="DirPath";Descending=$false} |
select -ExpandProperty DirPath | #-First 10 |
?{ #(gci $_ -Force).Count -eq 0 } | %{
Remove-Item $_ -Verbose # -WhatIf # uncomment to see the first pass of folders to be cleaned**
$_ | Out-File -Append -Encoding utf8 $delelteListTxt
sleep 0.1
}
# ** - The list you'll see from -WhatIf isn't a complete list because parent folders
# might also qualify after the first level is cleaned. The -WhatIf list will
# show correct breath, which is what I want to see before running the command.
To remove files older than 30 days:
get-childitem -recurse |
? {$_.GetType() -match "FileInfo"} |
?{ $_.LastWriteTime -lt [datetime]::now.adddays(-30) } |
rm -whatif
(Just remove the -whatif to actually perform.)
Follow up with:
get-childitem -recurse |
? {$_.GetType() -match "DirectoryInfo"} |
?{ $_.GetFiles().Count -eq 0 -and $_.GetDirectories().Count -eq 0 } |
rm -whatif
This worked for me.
$limit = (Get-Date).AddDays(-15)
$path = "C:\Some\Path"
Delete files older than the $limit:
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
Delete any empty directories left behind after deleting the old files:
Get-ChildItem -Path $path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse