Delete oldest versions of a directory in PowerShell - powershell

I have a list of directories which are formatted like version numbers and would like to find the N oldest directories and delete them. For example:
/1.2.3.4
/1.2.3.5
/1.2.3.6
I've tried a few things, but I can't quite seem to get where I need to go.
My first try was this:
ls directory | sort Name | select -first 5 | rm -r
However I'm not sure this is going to work in all circumstances, because this will (I presume) do a natural sort. Is that always going to return the correct results?
My next thought was that I could use System.Version to do my sorting. So I ended up with this:
ls directory | %{[System.Version]$_.Name } | sort | select -first 5 | ???
The problem is that I'm not sure how to tie the directory result to the sorting... What's the best way to do this?
gci \\directory produces
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 12/19/2011 5:19 PM 1.0.1052.54849
d---- 12/19/2011 5:29 PM 1.0.1053.54850
d---- 12/19/2011 5:36 PM 1.0.1054.54851
d---- 12/20/2011 2:11 PM 1.0.1056.54875
d---- 12/12/2011 10:39 AM 1.0.991.54625
d---- 12/12/2011 12:08 PM 1.0.992.54627
d---- 12/12/2011 12:22 PM 1.0.993.54628
d---- 12/12/2011 1:15 PM 1.0.994.54630
d---- 12/12/2011 2:45 PM 1.0.996.54636
d---- 12/12/2011 3:34 PM 1.0.997.54640
d---- 12/12/2011 3:48 PM 1.0.998.54641
gci \\directory | Sort-Object { $_Name -as [Version] } produces
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 12/12/2011 1:15 PM 1.0.994.54630
d---- 12/12/2011 12:22 PM 1.0.993.54628
d---- 12/12/2011 2:45 PM 1.0.996.54636
d---- 12/12/2011 3:48 PM 1.0.998.54641
d---- 12/12/2011 3:34 PM 1.0.997.54640
d---- 12/12/2011 12:08 PM 1.0.992.54627
d---- 12/19/2011 5:29 PM 1.0.1053.54850
d---- 12/19/2011 5:19 PM 1.0.1052.54849
d---- 12/19/2011 5:36 PM 1.0.1054.54851
d---- 12/12/2011 10:39 AM 1.0.991.54625
d---- 12/20/2011 2:11 PM 1.0.1056.54875
Does it matter that this is a network share? I'm confused as to why this isn't working... I did a quick sanity check and doing Array.Sort on versions I've created in a unit test are sorted correctly.

You can actually sort on an expression, which will keep your original objects.
Get-ChildItem $path |
Sort-Object { $_.Name -as [Version] } |
Select-Object -Last 1 |
Remove-Item
Will do the trick.
Hope this helps,

Natural sort is the order that you want. 1,2,3..10,11..instead of 1,10,11,2,3..
1..11 | %{$_.tostring()} | sort
Gives it in "ASCIIbetical" order, which is not the natural order we expect it to be in.
Based on what you were doing with version, I would say you can do like this, though it might be a bit over board:
gci directory | %{new-object psobject -p #{version=[version]($_.name);dir=$_ }} |
sort version | select -expand dir -first 5 | rm -r -whatif
or
gci directory | select #{e={[version] $_.name};l="version"}, #{e={$_};l="dir"} |
sort version | select -expand dir -first 5 | rm -r -whatif

Related

I want to check how many pdfs in a folder with Powershell with a function

I want to check how many pdf files a folder. For example in my C:\Temp\Test folder there are 6 pdf files and 6 txt files.
PS H:\> dir c:\temp\test
Directory: C:\temp\test
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 21/01/2022 10:05 PM 611 1.pdf
-a---- 26/01/2022 5:22 PM 0 1.txt
-a---- 22/01/2022 12:19 PM 3939 2.pdf
-a---- 26/01/2022 5:22 PM 0 2.txt
-a---- 08/01/2022 4:53 PM 27992 3.pdf
-a---- 26/01/2022 5:22 PM 0 3.txt
-a---- 08/01/2022 4:53 PM 27992 4.pdf
-a---- 26/01/2022 5:22 PM 0 4.txt
-a---- 22/01/2022 12:19 PM 3939 5.pdf
-a---- 26/01/2022 5:22 PM 0 5.txt
-a---- 21/01/2022 10:05 PM 611 6.pdf
If I use following PS, I got the right result 6
PS H:\> $directoryInfo = Get-ChildItem C:\Temp\Test | Where-Object {$_.Extension -eq ".pdf"}
$file_count = $directoryInfo.count
$file_count
6
However, when I use it like a function and deleted 2 pdfs in the folder. It still show as 6 pdfs. While, the $file_count in the function show as 4.
PS H:\> function CheckFileCount($Folder) {
$directoryInfo = Get-ChildItem $Folder | Where-Object {$_.Extension -eq ".pdf"}
$file_count = $directoryInfo.count
return $file_count
}
dir C:\Temp\Test
CheckFileCount C:\Temp\Test
Write-Host "There are $file_count PDFs in this folder."
Directory: C:\Temp\Test
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 26/01/2022 5:22 PM 0 1.txt
-a---- 26/01/2022 5:22 PM 0 2.txt
-a---- 08/01/2022 4:53 PM 27992 3.pdf
-a---- 26/01/2022 5:22 PM 0 3.txt
-a---- 08/01/2022 4:53 PM 27992 4.pdf
-a---- 26/01/2022 5:22 PM 0 4.txt
-a---- 22/01/2022 12:19 PM 3939 5.pdf
-a---- 26/01/2022 5:22 PM 0 5.txt
-a---- 21/01/2022 10:05 PM 611 6.pdf
4
There are 6 PDFs in this folder.
It looks like the function can't return $file_count value. What's wrong here? Thank you in advance.
Thank you all and I think its been solved.
Your function worked for me flawlessly when calling the function only.
I am adding a really good article about scopes in PowerShell:
PowerShellScopes
Your problem is:
Write-Host "There are $file_count PDFs in this folder."
Try saving the function result to a var like so:
$function_result = CheckFileCount 'C:\Software\example'
And then view the result:
Write-Host "There are $function_result PDFs in this folder."

How to read or process each line from a multiline output in powershell?

Start-Job -Name test123 -ScriptBlock { dir 'C:\Users\musuresh\Downloads' }
$output = Get-Job -Name test123 | Receive-Job | Out-String
$output | ForEach { "===$_===" }
output :
===
Directory: C:\Users\musuresh\Downloads
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 1/9/2018 9:19 AM en
-a--- 1/15/2018 2:18 PM 104681 3600nvram.pdf
-a--- 1/15/2018 3:24 PM 5763888 dlm.exe
-a--- 1/9/2018 9:19 AM 509280 help_en.exe
-a--- 1/15/2018 3:43 PM 15049960 HP_CLJ3600_64bit_HB.exe
-a--- 1/9/2018 10:47 AM 177776440 LJ-Ent-700-color-MFP-M775-Full-Solution-15315.exe
-a--- 1/9/2018 10:15 AM 18600800 upd-pcl6-x64-6.5.0.22695.exe
-a--- 1/9/2018 10:58 AM 48774144 zsu-1191239.exe
===
why can't i read/process each line one by one like skip the blank lines?

Rename Proposition to end of directory title

I have to change the name of approximately 600 directories to conform with the the business naming standards and it is proving to be quite complicated.
I will present the problem in a music directory format because it will be easier to explain and relate to.
If I wanted to rename all my Music Directories that have it will display prepositions at the end and not the start
Original
The Beatles
Desired Output
Beatles ,The
I have the following directories as examples
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 28/08/2016 3:51 PM Alabama Shakes
d---- 28/08/2016 3:51 PM Cat Stevens
d---- 28/08/2016 3:41 PM The Beatles
d---- 28/08/2016 3:42 PM The Brian Jonestown Massacre
d---- 28/08/2016 3:42 PM The Rolling Stones
d---- 28/08/2016 3:43 PM Them
d---- 28/08/2016 3:43 PM Them Rotten Vultures
d---- 28/08/2016 3:42 PM Van Morrison
I have managed to write something to filter the results down to just the ones I want renamed
Get-ChildItem -Filter "THE *"
However I don't know how to puss the logic to rename them all. I'm sure it's going to be recursive and it will use the Rename-Item function.
However I tried the following
Get-ChildItem -Filter "The *" -Recurse |
Rename-Item -NewName { $_.name -replace 'The ','' } |
Rename-Item -NewName { $_.name + " ,The" }
and got the following results
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 28/08/2016 3:51 PM Alabama Shakes
d---- 28/08/2016 3:41 PM Beatles
d---- 28/08/2016 3:42 PM Brian Jonestown Massacre
d---- 28/08/2016 3:51 PM Cat Stevens
d---- 28/08/2016 3:42 PM Rolling Stones
d---- 28/08/2016 3:43 PM Them
d---- 28/08/2016 3:43 PM Them Rotten Vultures
d---- 28/08/2016 3:42 PM Van Morrison
Any assistance would be kindly appreciated
Related question will the child item also detect the files? As only directories require to renamed
Managed to get it sorted with the following Code
It only modifies the directories and not the files contained in the directory.
Get-ChildItem -Filter "The *" -Directory|
?{ $_.PSIsContainer } |
Rename-Item -NewName { $_.name + " ,The" }
Get-ChildItem -Filter "The *" -Directory|
?{ $_.PSIsContainer } |
Rename-Item -NewName { $_.name -replace 'The ','' }

Copy-Item Not Creating All Directories

I have a directory that I want to recursively copy to anoth location for processing. It seems like the first directory is not getting created. The part of the program that does the copy is below:
# Read the source files to convert.
$lAllFiles = Get-ChildItem $lSrc -Exclude $lExcludedFiles
# Remove excluded directories
foreach ( $lExclusion in $lAllExcludedDirectories ) {
# If we have a wildcard use the -notlike operator to filter the list.
# Otherwise use an -ne operator.
if ( ($lExclusion -like '*' ) -or ($lExclusion -like '?' ) ) {
$lAllFiles = [array]$lAllFiles -notlike $lExclusion
} else {
$lAllFiles = [array]$lAllFiles -ne $lExclusion
}
}
# Start the log.
Start-Transcript -Path "$lJobLog"
# Copy the data to be converted to the conversion root.
ForEach ( $lFile in $lAllFiles ) {
Copy-Item $lFile $lCnv -Recurse -Force -ErrorAction SilentlyContinue -ErrorVariable +JobErrors -Verbose
}
.
.
.
When I compare the source and destination directories, it looks as though the first subdirectory is not created in the destination and the contents of the first sub directory are written to the destination.
As an example:
The Source:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 1/6/2013 9:30 AM AP-LEDGER
d---- 3/12/2014 9:28 AM AP.DETAIL
d---- 8/20/2014 9:33 PM AR-LEDGER
d---- 9/3/2011 9:58 AM lost+found
-a--- 5/13/2016 9:21 PM 32768 AP-BATCH-CTRL
-a--- 5/13/2016 8:39 PM 291293184 AP-LEDGER-XREF
-a--- 9/28/2015 3:14 PM 8425472 AP.JDE
-a--- 5/13/2016 8:39 PM 150700032 AP.LINK
-a--- 5/2/2016 3:30 PM 52224 AP.QUEUES
-a--- 5/13/2016 8:17 PM 743018496 AP.SUSP
-a--- 4/30/2016 9:11 PM 51222528 ARROLLARC
-a--- 4/30/2016 9:11 PM 102404096 ARROLLFWD
-a--- 5/14/2016 3:29 AM 1016950784 A_Pa
-a--- 5/14/2016 12:37 AM 238280704 A_Ra
-a--- 5/14/2016 3:16 AM 61423616 GL-CROSS
-a--- 5/14/2016 3:16 AM 175235072 GL-INDEX
-a--- 5/14/2016 3:16 AM 21512192 G_La
-a--- 5/14/2016 3:16 AM 224661504 X_AP.SUSP
-a--- 5/14/2016 3:29 AM 150089728 X_A_Pa
-a--- 5/14/2016 12:37 AM 63578112 X_A_Ra
The Destination:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 5/26/2016 10:53 PM AP.DETAIL
d---- 5/26/2016 10:53 PM AR-LEDGER
-a--- 5/13/2016 8:38 PM 1073737728 dat001
-a--- 5/13/2016 8:39 PM 682541056 dat002
-a--- 5/13/2016 8:39 PM 1004023808 idx001
-a--- 5/13/2016 8:37 PM 41234432 over001
Note the files in the directory should reside in the folder AP-LEDGER.
What is going on?
Does the -Recurse -Force not create the directories?
Check your Copy-Item, I've used something like this function to copy files/directories from one location to the next with exclusions.
Function Copy-Folder($Path,$DestinationPath,$Exclude = $exclude) {
# Get all the files to copy
$files = Get-ChildItem -Path $Path -Exclude $Exclude
# Copy files to destination folder.
foreach($file in $files) {
Copy-Item $file.FullName $DestinationPath -Recurse -Force
$filename = $file.Name
$copy = "$DestinationPath\$filename"
Write-Host "Copied: $filename to $copy"
}
}
You should also take a look at your logic for the removing excluded sub-directories

Write-Host - Lose new line formating

$output=$(dir)
Write-Host $output
The output I have is:
jboss-eap-5.1 jboss-eap-6.1 jboss-eap-6.1(2) Middleware Oracle TestSymlinksJboss TomcatA TomcatB Was7.0
Is there any way to have the normal output like this: echo $output
Directory: C:\Servers
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 9/18/2012 10:33 AM jboss-eap-5.1
d---- 5/9/2013 1:10 PM jboss-eap-6.1
d---- 8/22/2013 11:33 AM jboss-eap-6.1(2)
d---- 1/22/2013 2:29 PM Middleware
d---- 1/22/2013 2:25 PM Oracle
d---- 8/1/2013 2:43 PM TestSymlinksJboss
d---- 4/4/2013 4:25 PM TomcatA
d---- 7/22/2013 4:49 PM TomcatB
d---- 10/18/2012 3:00 PM Was7.0
I use Write-Host inside a function (so I don't want to use echo )
Thanks for any reply
Write-Host host does no formatting. It just displays the strings (or objects by coercing them to string) that you provide. Try this to get PowerShell to do some formatting for you:
Write-Host ($output | Out-String)