Count & Average files in a folder - powershell

Here is my code:
$folders = gci C:\NuGetRoot\NugetServer -Directory
foreach ($folder in $folders) {
#{ServerName=$env:COMPUTERNAME;
ProjectGroupID = $folder.Name;
NuGetPackageCount = (gci $folder.FullName\Packages -Include '*.txt') | %{$_.Size}.Count;
AverageSize = gci $folder.FullName -Recurse -Filter *.txt | measure-object -property length -average;
} | Export-Csv -Path d:\monitoring\NugetStatistics -NoTypeInformation -Append
}
I am looping through the folders in C:\NuGetRoot\NugetServer and then displaying the server name, the folder name (ProjectGroupID), package count of files ending in .nupkg in the 'packages folder for each folder, and the avg size of all of the files that are contained within the "packages" folder for each folder. The server name and ProjectGroupID display correctly. The count and average one aren't. I get the error:
gci : Second path fragment must not be a drive or UNC name. Parameter name:
path2
At line:5 char:30
+ NuGetPackageCount = (gci $folder.FullName\packages -Include '*.nupkg') | ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (D:\apps\nuget.ciodev.accenture.com:String) [Get-ChildItem], ArgumentException
+ FullyQualifiedErrorId : DirArgumentError,Microsoft.PowerShell.Commands.GetChildItemCommand*
I think it has something to do with the "\packages", because if I remove that it works, but I need to navigate to that folder because that's where the files are.

Instead of running Get-ChildItem 3 times like you are, you could run it once for all *.nupkg files, group them by the project they're associated with, and the grouping objects would have most the info you want right there. Consider this alternative:
$BasePath = "C:\NuGetRoot\NugetServer"
$folders = gci "$BasePath\*\Packages\..\*.nupkg" -Recurse
$(foreach ($folder in $folders|group #{e={$_.fullname.split('\')[0..3] -join '\'}}) {
[pscustomobject]#{
ServerName=$env:COMPUTERNAME
ProjectGroupID = Split-Path $folder.Name -Leaf
NuGetPackageCount = $folder.group.Where({$_.Directory -match 'Packages'}).count
AverageSize = $folder.Group | measure-object -property length -average | Select -Expand Average
}
}) | Export-CSV d:\monitoring\NugetStatistics -NoType -Append
I did have to be a little creative on the Count line's RegEx match to make sure I only got things within the Packages folder, but it works just fine. I created a test set of files:
C:\Temp\Test\ProjectA\Packages\File2.nupkg
C:\Temp\Test\ProjectA\Packages\File3.nupkg
C:\Temp\Test\ProjectA\File1.nupkg
C:\Temp\Test\ProjectA\File2.nupkg
C:\Temp\Test\ProjectA\File3.nupkg
C:\Temp\Test\ProjectB\Packages\File2.nupkg
C:\Temp\Test\ProjectB\Packages\File3.nupkg
C:\Temp\Test\ProjectB\File1.nupkg
C:\Temp\Test\ProjectC\Packages\File2 - Copy.nupkg
C:\Temp\Test\ProjectC\Packages\File2.nupkg
C:\Temp\Test\ProjectC\Packages\File3 - Copy.nupkg
C:\Temp\Test\ProjectC\Packages\File3.nupkg
C:\Temp\Test\ProjectC\File1.nupkg
C:\Temp\Test\ProjectC\File3.nupkg
When I ran the above script against the $BasePath of C:\Temp\Test I got these results:
ServerName ProjectGroupID NuGetPackageCount AverageSize
---------- -------------- ----------------- -----------
MININT-BMOSR3C ProjectA 2 161.2
MININT-BMOSR3C ProjectB 2 155.333333333333
MININT-BMOSR3C ProjectC 4 157.666666666667
I did want to point out that you run the Count against the Packages folder, but you run the AverageSize against the entire folder. You may want to add the .Where() statement from the Count line to the Average line if you only want files in the Packages folder for your average.
This should speed things up though since it only runs Get-ChildItem once, not once, and then 2 additional times for each folder, and it gets you the same data.

Related

Powershell returning different result for same query

I have directory that has subdirectories in this format number-number-longnumber
a few examples:
4-228-152283128912285
6-115-152336752325878
48-69-15232707627161
What I am trying to do is get the folder first modified and do some operations with it. Here's the query I ran:
$folder = Get-ChildItem -Path 'E:\moved-documents\' | Where-Object { $_.Name -match "[0-9]+-[0-9]+-[0-9]+" } | Sort-Object { $_.datemodified } | Select-Object -First 1;
This is giving me some folder,
4-29-1522845689658894
But when I run the same query, without the where filter, i.e.
$folder = Get-ChildItem -Path 'E:\moved-documents\' | Sort-Object { $_.datemodified } | Select-Object -First 1;
It is giving me some different folder:
68-79-1522275846040235
To make things even more odd, when I sort the folders in windows explorer by datemodified, none of these two folder come at top (or even bottom). Both of these (in fact, all 3 of these, 2 of these scripts and 1 that sorting in windows explorer itself) should return the same folder. Why are they returning different result and which one is correct?

Counting rows in 2 CSV files for comparison

I have a PowerShell script that almost does what I want.
Basically there are CSV file feeds that are written to a specific location and stored by year and month. I have to compare the number of rows between the two newest CSV files, as a large discrepancy indicates an issue.
Currently my script fetches the newest CSV file and returns the row count with no problems, but I can't work out how to get it to return the row count for the 2 newest files. It is likely due to the way I've structured the script:
$datemonth = (Get-Date).Month
$dateyear = (Get-Date).Year
## get latest csv files
$dir = "\\160.1.1.98\c$\Scheduled Task Software\ScheduledTask\Application Files\ScheduledTask_1_0_0_9\Files\$dateyear\$datemonth\SentFeedFiles"
$latest = Get-ChildItem -Path $dir |
Sort-Object LastAccessTime -Descending |
Select-Object -First 1
## get path to csv files, add headers and count number of rows.
$filepath = $dir + '\' + $latest
$CSVCOUNT = (Import-Csv $filepath -Header 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28).Count
If I change to -First 2 then I get the following error:
Import-Csv : Could not find file '\16.1.1.18\c$\Scheduled Task Software\ScheduledTask\Application Files\ScheduledTask_1_0_0_9\Files\2017\3\SentFeedFiles\lkrlkr200317.csv lkrlkr19017.csv'.
I know why I'm getting this error - its trying to join the two file names into one path. However, I'm at a loss of how to get around this. I'm thinking a loop may be required but I'm not sure where.
Chucked 3 CSV files in f:\tmp locally to test:
$dir = "F:\tmp"
$files = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 2
($files | Get-Content).Count
Import-Csv only deals with a single file as far as I remember - so you can't pass two file paths to it.
If you want to use Import-CSV (for ignoring headers etc), you can foreach file, but you have to pass the full path into it:
($files.FullName | % { Import-Csv -Path $_ }).Count
To get two separate results, do the following:
Include headers:
($files[0] | Get-Content).count
($files[1] | Get-Content).count
Exclude headers:
(Import-Csv -Path $files[0].FullName).Count
(Import-Csv -Path $files[1].FullName).Count

diskusage how to use in powershell

I am very new to powershell and writing a script which will go to each computer in windows domain and get the size of a user profile. I have tried below on a powershell script
$profileDir = "\\$computerName\c$\users\userProfile"
$fullProfile = (Get-ChildItem $profileDir -recurse -force | Measure-Object -property length -sum)
But on some computer it gives below error. The problem seems that some directories on the profile have a long path and get-ChildItem fails
Get-ChildItem : The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the directory name mu
st be less than 248 characters.
At C:\scripts\powershell\profiles.ps1:56 char:30
+ $fullProfile = (Get-ChildItem <<<< $profileDir -recurse -force | Measure-Obj
ect -property length -sum)
+ CategoryInfo : ReadError: (\\computerName...nt.IE5\RQT4K4JU:St
ring) [Get-ChildItem], PathTooLongException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChil
dItemCommand
##########################################################################################################################
At this point I tried using du.exe (diskusage) from SysInternals which works fine but I don't know how to take the output of du into a variable. I have below on my script
$dirSize = .\du.exe -c c:\temp |convertFrom-csv | select-object DirectorySize
write-host $dirSize
The output is
PS C:\scripts\powershell> .\profiles.ps1
#{DirectorySize=661531}
What I want the output to be like
PS C:\scripts\powershell> .\profiles.ps1
661531
What you have is a hashtable. You need to reference the item in that hashtable to get it's value. Instead of:
Write-Host $dirSize
Try:
$dirSize['DirectorySize']

Variable referencing. How to create arrays getting their names from elements of another array

This is as simplified version of what I'd like to achieve... I think it's called 'variable referencing'
I have created an array containing the content of the folder 'foo'
$myDirectory(folder1, folder2)
Using the following code:
$myDirectory= Get-ChildItem ".\foo" | ForEach-Object {$_.BaseName}
I'd like to create 2 arrays named as each folders, with the contained files.
folder1(file1, file2)
folder2(file1, file2, file3)
I tried the following code:
foreach ($myFolder in $myDirectory) {
${myFolder} = Get-ChildItem ".\$myFolders" | forEach-Object {$_.BaseName}
}
But obviously didn't work.
In bash it's possible create an array giving it a variable's name like this:
"${myForder[#]}"
I tried to search on Google but I couldn't find how to do this in Powershell
$myDirectory = "c:\temp"
Get-ChildItem $myDirectory | Where-Object{$_.PSIsContainer} | ForEach-Object{
Remove-Variable -Name $_.BaseName
New-Variable -Name $_.BaseName -Value (Get-ChildItem $_.FullName | Where-Object{!$_.PSIsContainer} | Select -ExpandProperty Name)
}
I think what you are looking for is New-Variable. Cycle through all the folders under C:\temp. For each folder make a new variable. It would throw errors if the variable already exists. What you could do for that is remove a pre-exising variable. Populate the variable with the current folders contents in the pipeline using Get-ChildItem. The following is a small explanation of how the -Value of the new variable is generated. Caveat Remove-Variable has the potiential to delete unintended variables depending on your folder names. Not sure of the implications of that.
Get-ChildItem $_.FullName | Where-Object{!$_.PSIsContainer} | Select -ExpandProperty Name
The value of each custom variable is every file ( not folder ). Use -ExpandProperty to just gets the names as strings as supposed to a object with Names.
Aside
What do you plan on using this data for? It might just be easier to pipe the output from the Get-ChildItem into another cmdlet. Or perhaps create a custom object with the data you desire.
Update from comments
$myDirectory = "c:\temp"
Get-ChildItem $myDirectory | Where-Object{$_.PSIsContainer} | ForEach-Object{
[PSCustomObject] #{
Hotel = $_.BaseName
Rooms = (Get-ChildItem $_.FullName | Where-Object{!$_.PSIsContainer} | Select -ExpandProperty Name)
}
}
You need to have at least PowerShell 3.0 for the above to work. Changing it for 2.0 is easy if need be. Create and object with hotel names and "rooms" which are the file names from inside the folder. If you dont want the extension just use BaseName instead of Name in the select.
This is how I did it at the end:
# Create an array containing all the folder names
$ToursArray = Get-ChildItem -Directory '.\.src\panos' | Foreach-Object {$_.Name}
# For each folder...
$ToursArray | ForEach-Object {
# Remove any variable named as the folder's name. Check if it exists first to avoid errors
if(Test-Path variable:$_.BaseName){ Remove-Variable -Name $_.BaseName }
$SceneName=Get-ChildItem ".\.src\panos\$_\*.jpg"
# Create an array using the main folder's name, containing the names of all the jpg inside
New-Variable -Name $_ -Value ($SceneName | Select -ExpandProperty BaseName)
}
And here it goes some code to check the content of all the arrays:
# Print Tours information
Write-Verbose "Virtual tours list: ($($ToursArray.count))"
$ToursArray | ForEach-Object {
Write-Verbose " Name: $_"
Write-Verbose " Scenes: $($(Get-Variable $_).Value)"
}
Output:
VERBOSE: Name: tour1
VERBOSE: Scenes: scene1 scene2
VERBOSE: Name: tour2
VERBOSE: Scenes: scene1

How do I use LastWriteTime when comparing files in different folder

This script compare FILE objects by Name, Length, and LastWriteTime.
cls
$Source= "C:\Source"
$Destination = "C:\Destination"
Compare-Object (ls $Source) (ls $Destination) -Property Name, Length, LastWriteTime | Sort-Object {$_.LastWriteTime} -Descending
Output:
Name Length LastWriteTime SideIndicator
---- ------ ------------- -------------
11.0.3127.0.txt 6 8/31/2013 10:01:19 PM <=
11031270.txt 0 8/31/2013 9:43:41 PM <=
11.0.3128.0.txt 13 8/31/20131:20:15PM =>
11.0.3129.0.txt 0 8/28/2013 11:34:38 AM <=
I need to create a script that retrive the current DB version and checks if single or multiple patches are available.
The way it works is the following:
Run a SQL Query against a DB
Store the SQL Info onto a fileName (eg.11.0.3128.0.txt) on C:\Destination
Compare the information in the .txt file against the files/patches present in the Source folder
List item
If Source folder contains older files/patches -- do nothing
If Source folder contain newer files, then copy those files to C:\NewPatchFolder
Then run a script to apply all the new patches
I already took care of #1, #2. I was planning to modify/add on to above script to simplify the steps in #3, #4 and #5.
Is it possible to modify above script to achive my goals as follow:
compare LastWriteTime from the files in C:\Source folder with the file in C:\Destination
copies the files in C:\Source folder to C:\NewPatchFolder if LastWriteTime is equal or greater then the LastWriteTime of the file on the C:\Destination folder
I wouldn't use Compare-Object for this. Try the following:
Get-ChildItem $Source | % {
$f = Join-Path $Destination $_.Name
if (Test-Path -LiteralPath $f) {
if ($_.LastWriteTime -ge (Get-Item $f).LastWriteTime) {
Move-Item $_.FullName 'C:\NewPatchFolder'
}
}
}