I have a directory whose subdirectories are all numbers:
./2856
./2357
./10198
and so on.
I'm trying to write a Powershell script that would return the largest subdirectory name smaller than X.
So in this example, for the input 3000 it should return 2856.
However what I've written so far looks very cumbersome to me, and I'm wondering how it can be shortened:
Get-ChildItem "$path" `
| ?{ $_.PSIsContainer } `
| Select-Object #{Name="AsInt"; Expression={[int]$_.Name}} `
| Select-Object -expand AsInt `
| ?{$_ -lt [int]$lessThanNumber} `
| measure-object -max `
| Select-Object -expand Maximum
I tried this with PowerShell v3:
$max = 3000
$cur = 0
ls -d | %{
# Potential for issues if the directory name cannot be cast to [int]
$name = ([int]$_.Name)
if (($name -gt $cur) -and ($name -le $max)) {
$cur = $name
}
}
($cur = 2856 at the end)
With PowerShell v3:
Get-ChildItem $path -dir | Select *,#{n='Number';e={[int]$_.Name}} |
Where Number -lt $lessThanNumber | Sort Number | Select -Last 1
If you have V3:
#(Get-ChildItem -Path $Path -Directory -Name |
ForEach-Object {$_ -as [int]}) -lt $LessThanNumber |
sort | select -last 1
You can try :
Get-ChildItem "$path" | Where-Object {$_.PSIsContainer -and [int]$_.name -le 3000} `
| Sort-Object -Property #{exp={[int]$_.name}} `
| Select-Object -Last 1
You can write it :
Get-ChildItem "$path" | ? {$_.PSIsContainer -and [int]$_.name -le 3000} `
| Sort -Property #{exp={[int]$_.name}} `
| Select -Last 1
If you want to avoid errors due to these directory names which are not integers :
Get-ChildItem "$path" | ? {$_.PSIsContainer -and ($_.name -as [int]) -le 3000} `
| Sort -Property #{exp={$_.name -as [int]}} `
| Select -Last 1
Yet another (v3) example. Only directory names that contain numbers are passed on and the Invoke-Expression cmdlet is used to evaluate the name into a number (no explicit cast is needed)
$x = 3000
Get-ChildItem -Directory | Where-Object {
$_.Name -notmatch '\D' -and (Invoke-Expression $_.Name) -lt $x
} | Sort-Object | Select-Object -Last 1
Related
$DiskCount = (Get-Disk | Where-Object {$_.BusType -eq "USB"}).Number.Count
if ($DiskCount -eq 1) {
filter Get-FirstResolvedPath {
(Get-Disk |
Where-Object {$_.BusType -eq "USB"} |
Get-Partition |
Get-Volume |
Where-Object {$null -ne $_.DriveLetter}
).DriveLetter + ':\' | Join-Path -ChildPath $_ -Resolve -ErrorAction SilentlyContinue
}
'Folder\Folder\reg\Start.reg' | Get-FirstResolvedPath
}
Is there another method to get full path to the file, if it stores on USB drive, the path is absolute, and we don't know USB disk letter?
I like your solution. I would write it differently, and you could shorten it a bit:
function Get-ResolvedPath {
param ([Parameter(ValueFromPipeline=1)]$Path)
Get-Disk |? BusType -ne USB | Get-Partition |% {Join-Path ($_.DriveLetter+":") $Path -R -EA Silent}
}
'Folder\Folder\reg\Start.reg' | Get-ResolvedPath | select -First 1
I've been searching for a script that simply lists folders in a share and their size. I found the following but I'm getting hung up on how to export the output I'm seeing to an easy to read CSV. It amazes me how something so simple has turned into something difficult. Suggestions welcome!
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"
}
Based on your script:
## C:\Users\UserName\Desktop\Test\SO_50359947.ps1
$colItems = Get-ChildItem "$($Env:USERPROFILE)\Desktop" |
Where-Object {$_.PSIsContainer} | Sort-Object
$data = ForEach ($i in $colItems){
$subFolderItems = Get-ChildItem $i.FullName -recurse -force -ea 0|
Where-Object {!$_.PSIsContainer} |
Measure-Object -Property Length -sum | Select-Object Sum
[PSCustomObject]#{
Folder = $i.FullName
Size = "{0,10:N2} MB" -f ($subFolderItems.sum / 1MB)
}
}
$data
#$data | Export-Csv "$($Env:USERPROFILE)\Desktop\your.csv" -NoType
Sample output (on a different tree)
> $data
Folder Size
------ ----
Q:\test\2018\03 0,37 MB
Q:\test\2018\04 0,83 MB
Q:\test\2018\05 383,57 MB
Uncomment the last line to write to a csv file.
Here is one way using custom objects:
Get-ChildItem "$home\Desktop" -Directory |
ForEach-Object {
$contents = Get-ChildItem -Path $_.FullName -Recurse
[PsCustomObject]#{
FolderName = $_.Name
SizeMB = [Math]::Round(($contents | Where-Object PsIsContainer -eq $false | Measure-Object -property Length -Sum).Sum / 1MB,2)
SubFolders = ($contents | Where-Object PsIsContainer -eq $true | Measure-Object).Count
Files = ($contents | Where-Object PsIsContainer -eq $false | Measure-Object).Count
}
}
This gives output like this:
FolderName SizeMB SubFolders Files
---------- ------ ---------- -----
Folder1 438.38 19 124
Folder2 34925.72 306 3779
To send this to CSV, simply append the following after the last bracket:
| Export-Csv "$home\Desktop\Folders.csv" -NoTypeInformation
There's a couple ways to do this, but I think the least confusing would be to simply add that information to the item using Add-Member. Then output the desired data via Export-Csv.
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" |
Where-Object {$_.PSIsContainer -eq $true} |
Sort-Object |
%{ Add-Member -InputObject $_ -NotePropertyName 'FolderSize' -NotePropertyValue (Get-ChildItem $_.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object -ExpandProperty Sum) -PassThru}
$colItems | Select FullName,FolderSize | Export-Csv -NoType
Please assign to some variable after getting all the file details from the desktop/any location and create the excel
$subFolderItems = Get-ChildItem "$home\Desktop" -Directory |
ForEach-Object {
$contents = Get-ChildItem -Path $_.FullName -Recurse
[PsCustomObject]#{
FolderName = $_.Name
SizeMB = [Math]::Round(($contents | Where-Object PsIsContainer -eq $false | Measure-Object -property Length -Sum).Sum / 1MB,2)
SubFolders = ($contents | Where-Object PsIsContainer -eq $true | Measure-Object).Count
Files = ($contents | Where-Object PsIsContainer -eq $false | Measure-Object).Count
}
}
$subFolderItems | out-file C:\Users\thiyagu.a.selvaraj\Desktop\PowerShell\FileSizeOutput.xls
In my opinion, using CSV for your use case isn't the best plan because (in my folder) there are files that are over 1000 MB, so csv will break up some file sizes. To make a tab delimited file, which can just as easily be parsed by most systems, simply modify your scripts output string.
I changed the -- to a tab character, and added an output at the end of the line
"{0}`t{1:N2} MB" -f $i.fullname,($subFolderItems.sum / 1MB) >> output.csv
The final script is below; you will probably need to change output.csv to your prefered output file location.
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
"{0}`t{1:N2} MB" -f $i.fullname,($subFolderItems.sum / 1MB) >> output.csv
}
If you don't insist on using pure powershell, on Windows 10 (sufficiently new) you can just call bash:
bash -c "du -h | sed 's/\s\+/,/'"
To show how it affects the resulting csv, a longer example:
bash -c "du -m | sed 's/\([0-9]\+\)\s\+\(.*\)/\1MB,\2/'"
I have a script that filters my logs, but the problem is that when I would like to delete everything else but certain files I get errors of Unrecognized escape sequence. I've been trying to split the values but it seems that nothing works. I also tried -exclude before, but didn't get it to work. It's supposed to remove all the other files but $result and $clr.
$files = #()
[xml]$photonconfig = Get-Content C:\Users\Administrator\Desktop\PhotonServer.config
$photonconfig.SelectNodes("Configuration/*") | Select-Object -Expand Name | % {
$_.Replace("xxx","")
} | ForEach {
$files+= Get-ChildItem C:\Users\Administrator\Desktop\log\log/*$_*.log |
sort -Property LastWriteTime -Descending |
Select-Object -First 3
}
$result = $files | Sort-Object LastAccessTime -Descending |
Select-Object -First 3
$clr = "PhotonCLR.log"
$all = Get-ChildItem C:\Users\Administrator\Desktop\log\log/* |
Where-Object { $_.Name -notmatch $result } |
Remove-Item
The second operand of the -match and -notmatch operators is a regular expression, not an array of file names. Use the -contains operator instead:
... | Where-Object { $result -notcontains $_.Name } | ...
On PowerShell v3 and newer you can also use the -notin operator, which feels a little more "natural" to most people:
... | Where-Object { $_.Name -notin $result } | ...
Note that for this to work you also need to expand the Name property when building $result:
$result = $files | Sort-Object LastAccessTime -Descending |
Select-Object -First 3 -Expand Name
I found a script to give me the size of each subfolder within a directory.
The problem is I don't know where to put the export-csv command to get that output in a CSV.
As a bonus I would also like to throw in a lastmodified property to those subfolders if possible. But I realize that may require a different script entirely.
Here is the script:
$startFolder = "C:\Scripts"
$colItems = (Get-ChildItem $startFolder | Measure-Object -property length -sum)
"$startFolder -- " + "{0:N2}" -f ($colItems.sum / 1MB) + " MB"
$colItems = (Get-ChildItem $startFolder -recurse | Where-Object {$_.PSIsContainer -eq $True} | Sort-Object)
foreach ($i in $colItems)
{
$subFolderItems = (Get-ChildItem $i.FullName | Measure-Object -property length -sum)
$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"
}
I've tried adding | export-csv c:\path to the very end, both inside and outside the bracket. I also tried adding it after the script command, ie c:\script.ps1 | export-csv. Each time I get the error that "An empty pipe element is not allowed."
If you want to process the output of a foreach loop with a pipeline you must either collect its output in a variable:
$results = foreach ($i in $colItems) {
...
}
$results | Export-Csv 'C:\path\to\your.csv' -NoType
or run it in an expression (i.e. in parentheses):
(foreach ($i in $colItems) {
...
}) | Export-Csv 'C:\path\to\your.csv' -NoType
However, personally I'd prefer ForEach-Object over foreach loops (for differences between the two see here), because the former does work with pipelines:
Get-ChildItem $startFolder -recurse |
Where-Object {$_.PSIsContainer -eq $True} |
Sort-Object |
ForEach-Object {
...
} |
Export-Csv 'C:\path\to\your.csv' -NoType
With that said, you may not want a loop in the first place. The Export-Csv cmdlet processes a list of objects and writes the properties of the input objects as fields to a CSV file. Since you already have object input you could simply select the properties you want to export:
Get-ChildItem $startFolder -recurse |
Where-Object {$_.PSIsContainer -eq $True} |
Sort-Object |̣
Select-Object FullName, LastWriteTime |
Export-Csv 'C:\path\to\your.csv' -NoType
Custom properties can be added for instance as calculated properties:
Get-ChildItem $startFolder -recurse |
Where-Object {$_.PSIsContainer -eq $True} |
Sort-Object |̣
Select-Object FullName, LastWriteTime, #{n='FolderSize';e={
Get-ChildItem $_.FullName |
Measure-Object -Property Length -Sum |
Select-Object -Expand Sum
}} |
Export-Csv 'C:\path\to\your.csv' -NoType
I'm trying to make the following PowerShell script more generic. I want to pass in an array of excludes rather than a fixed list. I can't figure out how to do this except my partial solution below:
ORIGINAL
This gets all the files in a path except a list of wildcard files or folders:
Get-ChildItem -Path "$sitePath" -Recurse | `
where {!$_.PSIsContainer } | `
Select -ExpandProperty FullName | `
Where {$_ -notlike "$sitePath\Custom\*"} | `
Where {$_ -notlike "$sitePath\Download\*"} | `
Where {$_ -notlike "$sitePath\Temp\*"} | `
Where {$_ -notlike "$sitePath\Portal\*"} | `
Where {$_ -notlike "$sitePath\web.config*"} | `
SELECT $_
PARTIAL SOLUTION
This is the best I've come up with. It allows me to create an array of wildcards called $excludeList, but is limited and is slightly slower:
$excludeList = #("$sitePath\Custom\*",
"$sitePath\Download\*",
"$sitePath\Portal\*",
"$sitePath\web.config*")
Get-ChildItem -Path "$sitePath" -Recurse | `
where {!$_.PSIsContainer } | `
Select -ExpandProperty FullName | `
Where {$_ -notlike $excludeList[0]} | `
Where {$_ -notlike $excludeList[1]} | `
Where {$_ -notlike $excludeList[2]} | `
Where {$_ -notlike $excludeList[3]} | `
Where {$_ -notlike $excludeList[4]} | `
Where {$_ -notlike $excludeList[5]} | `
Where {$_ -notlike $excludeList[6]} | `
Where {$_ -notlike $excludeList[7]} | `
Where {$_ -notlike $excludeList[8]} | `
Where {$_ -notlike $excludeList[9]} | `
Where {$_ -notlike $excludeList[10]} | `
SELECT $_
Is there a better way to pass an array in to the where clause? All the solutions I've found only allow non-wildcard matches.
Hope someone can help!
One approach would be to iterate over the items in the exclude list, and only include a path if it does not match any of the exclusions:
$excludeList = #("$sitePath\Custom\*",
"$sitePath\Download\*",
"$sitePath\Portal\*",
"$sitePath\web.config*")
Get-ChildItem -Path "$sitePath" -Recurse |
where { !$_.PSIsContainer } |
select -ExpandProperty FullName |
where { $path = $_; -not #($excludeList | ? { $path -like $_ }) }
If all of your excluded items follow the same pattern, you can also simplify the exclude list by moving the common pattern to the like call:
$excludeList = #('Custom','Download','Portal','web.config')
Get-ChildItem -Path "$sitePath" -Recurse |
where { !$_.PSIsContainer } |
select -ExpandProperty FullName |
where { $path = $_; -not #($excludeList | ? { $path -like "$sitePath\$_*" }) }
If you're willing to go with a regex instead, you can simplify this a lot:
$excludeList = [regex]::Escape("$sitePath\Custom\"),
[regex]::Escape("$sitePath\Download\"),
[regex]::Escape("$sitePath\Temp\") -join "|"
Get-ChildItem $sitePath -Recurse | `
where {!$_.PSIsContainer } | `
Select -ExpandProperty FullName | `
Where {$_ -notmatch $excludeList}
Not sure why you have the trailing Select $_, it is unnecessary AFAICT.
You should try -contains and -notcontains operators if you are trying to compare against an array.
$array = "name1","name2","name3"
#This will return false
$array -contains "name"
#this will return true
$array -notcontains "name"