Finding a file that has highest number in the filename using powershell - powershell

Sorry guys..I am new to powershell. Would be great if someone help with the following scenario:
I have couple of files in a folder c:\test
sample.x.x.1
sample.x.x.2
sample.x.x.3
sample.x.x.4
sample.x.x.5
I want to find the name of the file which has the highest number in its name in the given folder. In the above example, 5 is the highest number and the script should return the output filename as sample.x.x.5
Thanks in advance!

Sorting file names with numbers is quite a problem, as there are two ways. The first one sets them to alphabetical order. That is, 0, 1, 11, 111, 2,... The second one uses natural order. That is, 0, 1, 2, 11, 111.... This is surprisingly tricky and about every third programmer is confused with this.
There's a good answer already, which I'll refer like so,
# Create files 1..5
for($i=1;$i -le 5; ++$i) { set-content sample.x.x.$i -Value $null }
# Tricksy! Create file .10 to confuse asciibetic/natural sorting
set-content sample.x.x.10 -Value $null
ls # Let's see the files
Directory: C:\temp\test
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2015-09-28 10:29 0 sample.x.x.1
-a---- 2015-09-28 10:29 0 sample.x.x.10
-a---- 2015-09-28 10:29 0 sample.x.x.2
-a---- 2015-09-28 10:29 0 sample.x.x.3
-a---- 2015-09-28 10:29 0 sample.x.x.4
-a---- 2015-09-28 10:29 0 sample.x.x.5
# Define helper as per linked answer
$ToNatural = { [regex]::Replace($_, '\d+$', { $args[0].Value.PadLeft(20,"0") }) }
# Sort with helper and check the output is natural result
gci | sort $ToNatural -Descending | select -First 1
Directory: C:\temp\test
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2015-09-28 10:29 0 sample.x.x.10

Alphabetical sorting.
PS C:\Users\Gebb> #("sample.x.x.1", "sample.x.x.5", "sample.x.x.11") | sort
sample.x.x.1
sample.x.x.11
sample.x.x.5
Numerical sorting.
PS C:\Users\Gebb> #("sample.x.x.1", "sample.x.x.5", "sample.x.x.11") |
sort -Property #{Expression={[Int32]($_ -split '\.' | select -Last 1)}}
sample.x.x.1
sample.x.x.5
sample.x.x.11
Largest number.
PS C:\Users\Gebb> #("sample.x.x.1", "sample.x.x.5", "sample.x.x.11") |
sort -Property #{Expression={[Int32]($_ -split '\.' | select -Last 1)}} |
select -Last 1
sample.x.x.11

Related

PowerShell script to compare two files and merge the output the into a third file

I have have 2 files which I need to compare with PowerShell and generate the 3rd file with the merge output which contains all the lines from file1.txt and file2.txt.
This is an example of file1.txt and file2.txt:
file1.txt content
"Name","RcopyGroup","State","Type","Prov","UsrCPG","VSize_MB","Usr_Rsvd_MB","Usr_Used_Perc","Usr_Used_MB","Snp_Rsvd_MB","Snp_Used_Perc"
".srdata","-","normal","base","full","-","81920","81920","100.0","81920","0","0.0"
"admin","-","normal","base","full","-","12288","12288","100.0","12288","0","0.0"
"LUN1","-","normal","base","tpvv","SSD_CPG","1331200","1159552","85.6","1139240","17408","0.0"
"LUN2","-","normal","base","tpvv","FC_CPG","122880","82944","52.3","64304","17408","0.0"
"LUN3","-","normal","base","tpvv","SSD_CPG","71680","56320","73.7","52861","1024","0.0"
"LUN4","-","normal","base","tpvv","FC_CPG","40960","33792","77.9","31922","1024","0.0"
"LUN5","-","normal","base","tpvv","FC_CPG","10240","11264","99.7","10209","1024","0.0"
File2.txt Content
"Name","LunID","PresentTo","vvWWN"
"LUN1","10","Host1","60002AC0000000000000004400015537"
"LUN1","10","Host3","60002AC0000000000000004400015537"
"LUN1","110","Host2","60002AC0000000000000004400015537"
"LUN1","209","Host5","60002AC0000000000000004400015537"
"LUN1","110","Host4","60002AC0000000000000004400015537"
"LUN2","111","Host2","60002AC000000000080019FC000153AF"
"LUN3","110","Host2","60002AC000000000080019FD000153AF"
"LUN4","210","Host2","60002AC0000000000A004BC500015537"
"LUN5","211","Host2","60002AC0000000000A004BC700015537"
"LUN4","210","Host5","60002AC0000000000A004BC500015537"
"LUN5","211","Host5","60002AC0000000000A004BC700015537"
and i want the output on file3.txt to be like this
"Name","RcopyGroup","State","Type","Prov","UsrCPG","VSize_MB","Usr_Rsvd_MB","Usr_Used_Perc","Usr_Used_MB","Snp_Rsvd_MB","Snp_Used_Perc","PresentTo"
".srdata","-","normal","base","full","-","81920","81920","100.0","81920","0","0.0",""
"admin","-","normal","base","full","-","12288","12288","100.0","12288","0","0.0",""
"LUN1","-","normal","base","tpvv","SSD_CPG","1331200","1159552","85.6","1139240","17408","0.0","Host1;Host3;Host2;Host5;Host4"
"LUN2","-","normal","base","tpvv","FC_CPG","122880","82944","52.3","64304","17408","0.0","Host2"
"LUN3","-","normal","base","tpvv","SSD_CPG","71680","56320","73.7","52861","1024","0.0","Host2"
"LUN4","-","normal","base","tpvv","FC_CPG","40960","33792","77.9","31922","1024","0.0","Host2;Host5"
"LUN5","-","normal","base","tpvv","FC_CPG","10240","11264","99.7","10209","1024","0.0","Host2;Host5"
I have check the similar post in the forum Powershell compare two files and generate third file
but it is not producing the result which i required. Can someone help to think of a way to do this.
Thank you.
You can use Group-Object cmdlet to work out the hosts for each name and then add a new property to each of the new objects from file1 with the grouped data joined using -join ";".
$file1 = Import-Csv .\file1.csv
$file2 = Import-Csv .\file2.csv
# Group file2 by Name and output as a hashtable
$group2 = $file2 | Select-Object Name, PresentTo | Group-Object -Property Name -AsHashTable
$file1 | ForEach-Object {
# Add the new property (column) 'PresentTo' with the matching file2 Hosts joined by ';'
$_ | Add-Member -NotePropertyName PresentTo -NotePropertyValue ($group2.($_.Name).PresentTo -join ';') -PassThru
} | Export-Csv .\file3.csv
# have a look at our new file
Import-Csv .\file3.csv | Format-Table *
Output
Name RcopyGroup State Type Prov UsrCPG VSize_MB Usr_Rsvd_MB Usr_Used_Perc Usr_Used_MB Snp_Rsvd_MB Snp_Used_Perc PresentTo
---- ---------- ----- ---- ---- ------ -------- ----------- ------------- ----------- ----------- ------------- ---------
.srdata - normal base full - 81920 81920 100.0 81920 0 0.0
admin - normal base full - 12288 12288 100.0 12288 0 0.0
LUN1 - normal base tpvv SSD_CPG 1331200 1159552 85.6 1139240 17408 0.0 Host1;Host3;Host2;Host5;Host4
LUN2 - normal base tpvv FC_CPG 122880 82944 52.3 64304 17408 0.0 Host2
LUN3 - normal base tpvv SSD_CPG 71680 56320 73.7 52861 1024 0.0 Host2
LUN4 - normal base tpvv FC_CPG 40960 33792 77.9 31922 1024 0.0 Host2;Host5
LUN5 - normal base tpvv FC_CPG 10240 11264 99.7 10209 1024 0.0 Host2;Host5

Foreach loop not reading files in numeric order

I have below files which i am reading using a foreach loop.
$GetGeneratedFiles = Get-ChildItem -Path "C:\Script\LF*.csv" -recurse | % { $_.FullName }
C:\Script\LF_Batch_1.csv
C:\Script\LF_Batch_10.csv
C:\Script\LF_Batch_11.csv
C:\Script\LF_Batch_12.csv
C:\Script\LF_Batch_13.csv
C:\Script\LF_Batch_14.csv
C:\Script\LF_Batch_15.csv
C:\Script\LF_Batch_16.csv
C:\Script\LF_Batch_17.csv
C:\Script\LF_Batch_18.csv
C:\Script\LF_Batch_19.csv
C:\Script\LF_Batch_2.csv
C:\Script\LF_Batch_20.csv
C:\Script\LF_Batch_21.csv etc...upto LF_Batch_.96.csv
Problem is it is reading the files like above not 1,2,3...and so on.
Please need idea how to read in ordered way
Solved using below approach
$GetGeneratedFiles = Get-ChildItem -Path "C:\Script\LF*.csv" -recurse | % { $_.FullName }
$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }
$GetGeneratedFiles = $GetGeneratedFiles | Sort-Object $ToNatural
Thanks #vonPryz for the reference.
Another way. It's funny how I just did another answer similar to this. A numeric sort on the names.
echo hi | set-content (1,2,10,20 | % tostring LF_Batch_0\.csv)
dir | sort {[void]($_ -match '\d+'); [int]$matches.0}
Directory: C:\Users\js\foo
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/17/2020 9:14 AM 4 LF_Batch_1.csv
-a--- 9/17/2020 9:14 AM 4 LF_Batch_2.csv
-a--- 9/17/2020 9:14 AM 4 LF_Batch_10.csv
-a--- 9/17/2020 9:14 AM 4 LF_Batch_20.csv

move files by names (variable last letter) to folder

We have a systeem at work where we change the last letter of the name of a file if we make a new version. We change it alphabetic and O is the firstone and then A,B,C,...
But the older versions don't need to stay in that folder any more. I am looking for a "simpel" solution in powershell to move this older files to a folder "old".
I don't know how to start in powershell (except to move to the right folder) and don't know if it is possible.
Any suggestions can help.
Set-Location -Path "Z:\PDF\2018\18-00190 StBV THV Depret Franki\2D"
Get-ChildItem | Sort-Object -Property name
In PowerShell you tend to work a lot with pipelines, creating sequences of objects and filtering, projecting or otherwise manipulating them along the way. Your problem could be solved as follows in a few steps (whether they're simple or not remains to be seen, but your requirements necessitate some custom code):
First group all files by their base file name without the suffix
Get-ChildItem -File | Group-Object { $_.Basename -creplace '_[A-O](?=$|\.)' }
This creates a grouping key which is basically the file name without the last-changed suffix. Then we have for each group all revisions that have been created for that file. E.g. for your file names 1409-EM-M-PL-7000_A.dwg.pdf you'd get a group named 1409-EM-M-PL-7000.dwg.pdf containing all versions of that file.
I'm assuming here that no letter beyond O will actually be used, but you can adapt the regex if necessary.
Sort the revisions in order:
ForEach-Object {
$_.Group | Sort-Object { $_.Basename -creplace '_O(?=$|\.)', '_0' } -Descending
}
We replace the _O temporarily with _0 for sorting here to get the correct order since it's the oldest but would usually appear as the latest version.
We also sort descending here (so we get the latest versions first) to make the next step easier, since we actually want to grab the files we have to move, not those we want to retain.
Retain the latest n versions by grabbing every file except the latest 3 in this case:
Select-Object -Skip 3
Move the remaining files to old:
Move-Item -Destination old
Putting it all together:
Get-ChildItem -File |
Group-Object { $_.Basename -creplace '_[A-O](?=$|\.)' } |
ForEach-Object {
$_.Group |
Sort-Object { $_.Basename -creplace '_O(?=$|\.)', '_0' } -Descending |
Select-Object -Skip 3
} |
Move-Item -Destination old
Stick a -WhatIf on the MoveItem to see what's being done without actually changing anything. As an example:
H:\Stuff\54664753> ls
Directory: H:\Stuff\54664753
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2019-02-13 10:10 1 old
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_A.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_B.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_C.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_D.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_O.dwg.pdf
-a---- 2019-02-13 10:11 0 file2_A.pdf
-a---- 2019-02-13 10:11 0 file2_O.pdf
-a---- 2019-02-13 10:11 0 file3_O.xml
-a---- 2019-02-13 10:11 0 file_A.txt
-a---- 2019-02-13 10:11 0 file_B.txt
-a---- 2019-02-13 10:11 0 file_C.txt
-a---- 2019-02-13 10:11 0 file_O.txt
H:\Stuff\54664753> Get-ChildItem |
>>> Group-Object { $_.Basename -creplace '_[A-O](?=$|\.)' } |
>>> ForEach-Object {
>>> $_.Group |
>>> Sort-Object { $_.Basename -creplace '_O(?=$|\.)', '_0' } -Descending |
>>> Select-Object -Skip 2
>>> } |
>>> Move-Item -Destination old -Whatif
What if: Performing the operation "Move File" on target "Item: H:\Stuff\54664753\1409-EM-M-PL-7000_B.dwg.pdf Destination: H:\Stuff\54664753\old\1409-EM-M-PL-7000_B.dwg.pdf".
What if: Performing the operation "Move File" on target "Item: H:\Stuff\54664753\1409-EM-M-PL-7000_A.dwg.pdf Destination: H:\Stuff\54664753\old\1409-EM-M-PL-7000_A.dwg.pdf".
What if: Performing the operation "Move File" on target "Item: H:\Stuff\54664753\1409-EM-M-PL-7000_O.dwg.pdf Destination: H:\Stuff\54664753\old\1409-EM-M-PL-7000_O.dwg.pdf".
What if: Performing the operation "Move File" on target "Item: H:\Stuff\54664753\file_A.txt Destination: H:\Stuff\54664753\old\file_A.txt".
What if: Performing the operation "Move File" on target "Item: H:\Stuff\54664753\file_O.txt Destination: H:\Stuff\54664753\old\file_O.txt".
You can also try out the individual steps by shortening the pipeline appropriately, e.g. only the initial grouping:
H:\Stuff\54664753> Get-ChildItem |
>>> Group-Object { $_.Basename -creplace '_[A-O](?=$|\.)' }
Count Name Group
----- ---- -----
1 old {old}
5 1409-EM-M-PL-7000.dwg {1409-EM-M-PL-7000_A.dwg.pdf, 1409-EM-M-PL-7000_B.dwg.pdf, 1409-EM-M-PL-7000_C.dwg.pdf, 1409-EM-M-PL-7000_D.dwg.pdf...}
2 file2 {file2_A.pdf, file2_O.pdf}
1 file3 {file3_O.xml}
4 file {file_A.txt, file_B.txt, file_C.txt, file_O.txt}
Or grouping and sorting, but not the rest:
H:\Stuff\54664753> Get-ChildItem -File |
>>> Group-Object { $_.Basename -creplace '_[A-O](?=$|\.)' } |
>>> ForEach-Object {
>>> $_.Group |
>>> Sort-Object { $_.Basename -creplace '_O(?=$|\.)', '_0' } -Descending
>>> }
Directory: H:\Stuff\54664753
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_D.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_C.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_B.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_A.dwg.pdf
-a---- 2019-02-13 10:02 0 1409-EM-M-PL-7000_O.dwg.pdf
-a---- 2019-02-13 10:11 0 file2_A.pdf
-a---- 2019-02-13 10:11 0 file2_O.pdf
-a---- 2019-02-13 10:11 0 file3_O.xml
-a---- 2019-02-13 10:11 0 file_C.txt
-a---- 2019-02-13 10:11 0 file_B.txt
-a---- 2019-02-13 10:11 0 file_A.txt
-a---- 2019-02-13 10:11 0 file_O.txt

Get file which is closest to a given date

I need a script which will get me the file which is closest to a given date, and need some help please.
For example:
$GivenDate = [datetime]"06/26/2017 10:30"
Get-ChildItem $backupDirectory -Filter "*.diff"
Output looks like this:
Mode LastWriteTime Length Name
-a---- 25.06.2017 15:30 506368 db1_backup_2017_06_25_153001_5520722.diff
-a---- 26.06.2017 7:30 1597952 db1_backup_2017_06_26_073001_6387310.diff
-a---- 26.06.2017 9:30 675840 db1_backup_2017_06_26_093001_6217913.diff
-a---- 26.06.2017 11:30 657408 db1_backup_2017_06_26_113001_1234104.diff
-a---- 26.06.2017 13:30 675328 db1_backup_2017_06_26_133000_9901392.diff
-a---- 26.06.2017 15:30 673792 db1_backup_2017_06_26_153001_5430241.diff
How can I select the file that is closest to to $givenDate?
Calculate a TimeSpan between the LastWriteTime property value and your $GivenDate, then sort on the absolute value (the duration) of the timespan:
$Closest = Get-ChildItem $backupDirectory -Filter *.diff |Sort {(New-TimeSpan $GivenDate $_.LastWriteTime).Duration()} |Select -First 1
variation of #Mathias R. Jessen solution (duration and timespan are not necessary)
Get-ChildItem $backupDirectory -file -Filter *.diff | sort {($GivenDate - $_.LastWriteTime)} | Select -First 1

How to split string and rename files in PowerShell?

I want to bulk rename the files in my folder, and all of them have the format of FilenameYeara\b.pdf, for example, TestData2001a.pdf, File2015b.pdf. I want to rename all of them to something like [Yeara\b]Filename, such as [2001a]TestData. The problem is that I don't know how can I split my filename into two parts (actually three if we count the extension, .pdf part), such that I put that second part as the first part of the file name.
Get-ChildItem | Rename-Item {$_.name -replace ‘current’, ’old’ }
How can I achieve this?
This does the regex match "anything, four digits, one character, .pdf" and replaces it with those items in the new ordering.
PS D:\t> gci | ren -newname { $_ -replace '(.*)(\d{4})(.)\.pdf', '[$2$3]$1.pdf' }
Directory: D:\t
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 13/05/2016 02:54 0 File2015b.pdf
-a--- 13/05/2016 02:53 0 TestData2001a.pdf
becomes
Directory: D:\t
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 13/05/2016 02:53 0 [2001a]TestData.pdf
-a--- 13/05/2016 02:54 0 [2015b]File.pdf
(Maybe try it with -Whatif before running for real)
This should get you started
$Matches.Clear()
Get-Item | % {
$_.BaseName -match "(\D+)([0-9]{4}[ab])"
Rename-Item -Path $_.FullName -NewName "$($Matches[2])$($Matches[1])$($_.Extension)"
}