Get all directories in directory that has a numeric name with powershell - powershell

I will start off by saying I am new to powershell...
I can get all the directories in a specified directory but I need to only get the directories that their names are numeric. Example:
1 - include
2 - include
3 - include
10 - include
LastFailed - exclude
I also will need to order them in sequential order for later processing.
Here is what I have so far:
$Dirs = Get-ChildItem -Path ..\..\..\builds -Attributes D
foreach($D in $Dirs)
{
Write-Host $D.Name
}

To get a list of just the folders that have numeric names, you can do this:
$Path = '..\..\..\builds'
$Dirs = Get-ChildItem -Path $Path -Attributes D |
Where-Object { $_.Name -match '^\d+$' } |
Sort-Object
$Dirs
This code above uses the Where-Object clause to filter out only the folders that have a numeric-only name.
It does this by using the -match operator and the regular expression ^\d+$ where:
`^` --> start at the beginning of the line
`\d` --> look for numeric values (0-9)
`+` --> there must be 1 or more numeric values present
`$` --> the end of the string
Instead of \d it could also have been written using [0-9]
The code ends with Sort-Object because the OP wants the list of folders sorted.
because we do not tell what property to sort on here, Powershell defaults to the Name.
The above will produce a list like:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 18-8-2018 16:13 1
d----- 18-8-2018 16:13 10
d----- 18-8-2018 16:13 2
d----- 18-8-2018 16:13 5
As you can see, the list is sorted by property Name, but.. the numbers were treated as strings, so '10' follows '1'.
What we most likely want to do here is to have the Sort-Object treat the names as real Numbers, not strings.
To fix that we add the Select-Object cmdlet in order to create our own objects with properties we need.
We can get standard properties of the DirInfo object that is coming through the pipeline, AND it is possible to create a new calculated property.
Here I add the calculated property called SortIndex that is simply the numeric value converted from the Name, which we will then use for the numeric sort:
$Dirs = Get-ChildItem -Path $Path -Attributes D |
Where-Object { $_.Name -match '^\d+$' } |
Select-Object -Property FullName, Name, LastWriteTime, #{Name = 'SortIndex'; Expression = {[int]$_.Name} } |
Sort-Object -Property SortIndex
$Dirs
Now it produces an array of objects that when printed to console looks like this:
FullName Name LastWriteTime SortIndex
-------- ---- ------------- ---------
D:\Temp\1 1 18-8-2018 16:13:22 1
D:\Temp\2 2 18-8-2018 16:13:25 2
D:\Temp\5 5 18-8-2018 16:13:28 5
D:\Temp\10 10 18-8-2018 16:13:31 10
Hope this helps.

You can utilize Where-Object to filter your collection:
$dirs = Get-ChildItem -Path ..\..\..\builds -Directory |
Where-Object -Property Name -Match '\d'
This operation uses regex to match the digit class.
If you meant you only want the directory names that start with a digit, you can use an anchor:
'^\d'
and lastly, you can sort them with Sort-Object:
$dirs = Get-ChildItem -Path ..\..\..\builds -Directory |
Where-Object -Property Name -Match '^\d' |
Sort-Object -Property Name
You can further enhance this sort by being explicit about the starting digit:
-Property {[int]($_.Name -split '[^\d]')[0]}
In this context, we're splitting on the first non-digit character and capturing the first fragment that was split off (which should be the number represented as a string, so we cast to an integer).

Related

Edit an object non-destructively in PowerShell

I am trying to format the resulting object without destroying it. But all my efforts and research has failed me. Any tips are welcome.
My code looks like this:
Set-Location 'C:\Temp'
$Files = Get-ChildItem -File | Select-Object FullName, Length
And what I get, is this:
FullName Length
-------- ------
C:\Temp\CleanupScript.txt 10600
C:\Temp\Columns.csv 4214
C:\Temp\Content.html 271034
C:\Temp\Content.txt 271034
C:\Temp\DirSizes.csv 78
What I want is this:
FullName Length
-------- ------
Temp\CleanupScript.txt 10600
Temp\Columns.csv 4214
Temp\Content.html 271034
Temp\Content.txt 271034
Temp\DirSizes.csv 78
When I tried this:
$Files = Get-ChildItem -File | Select-Object FullName, Length | % { $_.FullName.Remove(0, 3) }
I got the right result, but I lost the Length column.
PS C:\Temp> $Files
Temp\CleanupScript.txt
Temp\Columns.csv
Temp\Content.html
Temp\Content.txt
Temp\DirSizes.csv
Please help.
Big thanks
Patrik
The easiest way to do this is to construct the property you want in the Select command, such as:
$Files = Get-ChildItem -File | Select #{l='FullName';e={$_.FullName.Substring(3)}},Length
The format for this is a hashtable with two entries. The keys are lable (or name), and expression. You can shorten them to l (or n), and e. The label entry defines the name of the property you are constructing, and the expression defines the value.
If you want to retain all of the original methods and properties of the objects you should add a property to them rather than using calculated properties. You can do that with Add-Member as such:
$Files = GCI -File | %{Add-Member -inputobject $_ -notepropertyname 'ShortPath' -notepropertyvalue $_.FullName.Substring(3) -PassThru}
Then you can use that property by name like $Files | FT ShortPath,Length -Auto, while still retaining the ability to use the file's methods like Copy() and what not.
I would recommend using a calculated property and Split-Path -NoQualifier; e.g.:
Get-ChildItem -File | Select-Object `
#{Name = "NameNoQualifier"; Expression = {Split-Path $_.FullName -NoQualifier}},
Length
For help on calculated properties, see the help for Select-Object.
(Aside: To correct your terminology a bit, this is not modifying objects non-destructively but rather outputting new objects containing the properties you want formatted how you want them.)

Trying to display the contents of a folder including the size, date modified, and length

This script is working so far, but I need the size to be in KB. It works as well, but when I call for a list of files it just outputs the KB size below the list, instead of within the list of itself.
$fileList = Get-ChildItem "E:\"
foreach($file in $fileList) {
ls $file | select -Property fullName, LastWriteTime, length
}
foreach($file in $fileList) {
Write-Host((Get-Item $file).length/1KB) | Sort-Object fullName, LastWriteTime, length
}
I want to see how I can get the KB size to populate the list under length, instead of the default file size being displayed.
You can Select-Object to get only the properties you want:
Get-ChildItem "E:\" | Select-Object fullName,LastWriteTime,Length
FullName LastWriteTime Length
-------- ------------- ------
C:\folder\cluster_totals.xlsx 14/12/2017 15:11:40 9382
C:\folder\IAG.xlsx 23/11/2017 11:05:12 13054
C:\folder\Offline.xml 30/10/2017 16:14:24 21503
And then use Calculated Properties to change the formatting of the Length property into KB.
Get-ChildItem "E:\" | Select-Object fullName,LastWriteTime,#{Name="KB";Expression={"{0:N0}" -f ($_.Length / 1KB)}}
Which will give you something like this:
FullName LastWriteTime KBytes
-------- ------------- ------
C:\folder\cluster_totals.xlsx 14/12/2017 15:11:40 9
C:\folder\IAG.xlsx 23/11/2017 11:05:12 13
C:\folder\Offline.xml 30/10/2017 16:14:24 21

Using Group-Object : How to Keep x number of each directory and delete all others [duplicate]

I have a network share with 20.000 XML files in the format
username-computername.xml
There are duplicate entries in the form of (when a user received a new comptuer)
user1-computer1.xml
user1-computer2.xml
or
BLRPPR-SKB52084.xml
BLRSIA-SKB50871.xml
S028DS-SKB51334.xml
s028ds-SKB52424.xml
S02FL6-SKB51644.xml
S02FL6-SKB52197.xml
S02VUD-SKB52083.xml
Since im going to manipulate the XMLs later I can't just dismiss properties of the array as at the very least I need the full path. The aim is, if a duplicate is found, the one with the newer timestamp is being used.
Here is a snipet of the code where I need that logic
$xmlfiles = Get-ChildItem "network share"
Here I'm just doing a foreach loop:
foreach ($xmlfile in $xmlfiles) {
[xml]$xmlcontent = Get-Content -Path $xmlfile.FullName -Encoding UTF8
Select-Xml -Xml $xmlcontent -Xpath " "
# create [pscustomobject] etc...
}
Essentially what I need is
if ($xmlfiles.Name.Split("-")[0]) - duplicate) {
# select the one with higher $xmlfiles.LastWriteTime and store either
# the full object or the $xmlfiles.FullName
}
Ideally that should be part of the foreach loop to not to have to loop through twice.
You can use Group-Object to group files by a custom attribute:
$xmlfiles | Group-Object { $_.Name.Split('-')[0] }
The above statement will produce a result like this:
Count Name Group
----- ---- -----
1 BLRPPR {BLRPPR-SKB52084.xml}
1 BLRSIA {BLRSIA-SKB50871.xml}
2 S028DS {S028DS-SKB51334.xml, s028ds-SKB52424.xml}
2 S02FL6 {S02FL6-SKB51644.xml, S02FL6-SKB52197.xml}
1 S02VUD {S02VUD-SKB52083.xml}
where the Group property contains the original FileInfo objects.
Expand the groups in a ForEach-Object loop, sort each group by LastWriteTime, and select the most recent file from it:
... | ForEach-Object {
$_.Group | Sort-Object LastWriteTime -Desc | Select-Object -First 1
}

Filtering files by partial name match

I have a network share with 20.000 XML files in the format
username-computername.xml
There are duplicate entries in the form of (when a user received a new comptuer)
user1-computer1.xml
user1-computer2.xml
or
BLRPPR-SKB52084.xml
BLRSIA-SKB50871.xml
S028DS-SKB51334.xml
s028ds-SKB52424.xml
S02FL6-SKB51644.xml
S02FL6-SKB52197.xml
S02VUD-SKB52083.xml
Since im going to manipulate the XMLs later I can't just dismiss properties of the array as at the very least I need the full path. The aim is, if a duplicate is found, the one with the newer timestamp is being used.
Here is a snipet of the code where I need that logic
$xmlfiles = Get-ChildItem "network share"
Here I'm just doing a foreach loop:
foreach ($xmlfile in $xmlfiles) {
[xml]$xmlcontent = Get-Content -Path $xmlfile.FullName -Encoding UTF8
Select-Xml -Xml $xmlcontent -Xpath " "
# create [pscustomobject] etc...
}
Essentially what I need is
if ($xmlfiles.Name.Split("-")[0]) - duplicate) {
# select the one with higher $xmlfiles.LastWriteTime and store either
# the full object or the $xmlfiles.FullName
}
Ideally that should be part of the foreach loop to not to have to loop through twice.
You can use Group-Object to group files by a custom attribute:
$xmlfiles | Group-Object { $_.Name.Split('-')[0] }
The above statement will produce a result like this:
Count Name Group
----- ---- -----
1 BLRPPR {BLRPPR-SKB52084.xml}
1 BLRSIA {BLRSIA-SKB50871.xml}
2 S028DS {S028DS-SKB51334.xml, s028ds-SKB52424.xml}
2 S02FL6 {S02FL6-SKB51644.xml, S02FL6-SKB52197.xml}
1 S02VUD {S02VUD-SKB52083.xml}
where the Group property contains the original FileInfo objects.
Expand the groups in a ForEach-Object loop, sort each group by LastWriteTime, and select the most recent file from it:
... | ForEach-Object {
$_.Group | Sort-Object LastWriteTime -Desc | Select-Object -First 1
}

powershell filter to remove .pdf extension in the name of a file

I am trying to use powershell to get all child elements in a folder the code I am using is
Get-ChildItem -Recurse -path C:\clntfiles
this code gives output like
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 4/29/2015 9:11 AM 6919044 HD 100616 Dec2014.pdf
-a--- 5/1/2015 11:42 AM 7091019 HD 101642 Jan2015.pdf
I don't want Mode lastWriteTime Length and name of file without .pdf extension
the output should be like
Dec2014
Jan2015
I am not sure how to filter that. please advise
I'll start by posting something similar to Leptonator's answer, but simplified by using the Select-Object command (alias Select used in code because it's habit, and I'm lazy).
$files = Get-ChildItem -Recurse -path C:\clntfiles | Select -ExpandProperty BaseName
Now that gets you the file names without extension. But, you actually asked for only part of the file names, as the first file name is "HD 100616 Dec2014.pdf" and you specified that you actually only want "Dec2014" to be returned. We can do that a couple different ways, but my favorite of them would be a RegEx match (because RegEx is awesome, and I think the LastIndexOf/SubString combo is overly complicated imho).
So, a RegEx match of "\w+$" will get what you want. That is broken down like this:
\w means any letter or number
+ means 1 or more of them
$ means the end of the string/line
So that's 1 or more alpha-numeric characters at the end of the string. We pipe our array of file names into a ForEach-Object loop (alias ForEach used out of habit), and then we have:
$Files | ForEach{ [RegEx]::Matches($_,"\w+$")}
Now, this outputs a [System.Text.RegularExpressions.Match] object, which is more than you want, but it does have a property Value which is exactly what you asked for! So we use Select -Expand again for that property and the output is precisely what you asked for:
$files = Get-ChildItem -Recurse -path C:\clntfiles | Select -ExpandProperty BaseName
$files | ForEach{[regex]::Matches($_,"\w+$")} | Select -Expand Value
RegEx matches are really handy, and if you learn about them you can simplify that quite a bit more like this:
gci C:\clntfiles -Rec | ?{$_.BaseName -match "(\w+)$"} | %{$Matches[1]}
That one line, as well as the two line code above it both should output:
Dec2014
Jan2015
Something like this should do it for you..
$files = Get-ChildItem -Recurse -path C:\clntfiles
if ($files -ne $null)
{
foreach ($file in $files)
{
$file.BaseName
}
}
In my folder, it shows:
> 2014-03-28_exeresult_file
> 2014-03-30_exeresult_file
> 2014-03-31_exeresult_file
> 2014-04-02_exeresult_file
> 2014-04-03_exeresult_file
> 2014-04-04_exeresult_file
> 2014-04-06_exeresult_file
> 2014-04-08_exeresult_file
and are indeed .txt files
Hope this helps!
Use the following Get-ChildItem -Recurse -name -path C:\clntfiles. This will get you only the file names.
Working solution:
$names = Get-ChildItem -name
foreach($n in $names) {$n.Substring(0,$n.IndexOf("."))}
You can also use LastIndexOf if part of the file name is .