Extract Path from FullName property into results - powershell

I have this script:
Get-ChildItem -Path R:\MyFolder\archive -Recurse |
>> Sort-Object -Property LastAccessTime |
>> Select-object -Property Name, FullName
Rather than FullName I'd like the Path without the files name so I tried to adapt as follows:
Get-ChildItem -Path R:\MyFolder\archive -Recurse |
>> Sort-Object -Property LastAccessTime |
>> Select-object -Property Name, [System.IO.Path]::GetDirectoryName(fullname)
I'm obviously very new to PS - is it clear what I am attempting, and failing, to do?

Generally, Get-ChildItem cmdlet returns objects of different type:
System.IO.FileInfo
System.IO.DirectoryInfo
Unfortunately, on the latter you do not find property called DirectoryName. You can use Get-ChildItem -File (the -File parameter eliminates such objects), or use calculated property as follows:
$rootPath = 'R:\MyFolder\archive'
Get-ChildItem -Path $rootPath -Recurse |
Sort-Object -Property LastAccessTime |
Select-Object -Property Name,
#{ Name = 'DirectoryName';
Expression = { $_.FullName | Split-Path }}
For explanation, read in Select-Object => Parameters:
-Property
Specifies the properties to select. These properties are added as
NoteProperty members to the output objects. Wildcards are permitted.
The value of the Property parameter can be a new calculated
property. To create a calculated property, use a hash table.
Valid keys are:
Name (or Label):
Expression or

How about :
Get-ChildItem -Path 'R:\MyFolder\archive' -Recurse | Sort-Object -Property LastAccessTime | Select-object -Property Name, DirectoryName
?

Related

Powershell: how to loop through folders and execute the same code for each of the folders

Currently I have a script that will sort files in a folder (on their lastwritetime), keep the latest file and move the other files to a different folder. This works correctly:
Get-ChildItem "\\networkfolder\RawData\2_ActionData_Prep\CustomerA\" -Recurse -Filter "*.rpt" -File |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -Skip 1 |
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"
The problem is that I have several 'customer' folders and I want to execute the code above in each of those folders.
I tried the following:
$CustomerFolders = Get-ChildItem -Path "\\networkfolder\RawData\2_ActionData_Prep\" -Directory -Recurse
foreach ($folder in $CustomerFolders) {Get-ChildItem -Filter "*.rpt" -File | Sort-Object -Property LastWriteTime -Descending |
Select-Object -Skip 1 |
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"}
When I execute this script, nothing happens. Also no error comes up. Hopefully someone could help me on this.
Santiago Squarzon noticed that a $folder was missing, so I added $folder in loop for Get-Childitem:
$CustomerFolders = Get-ChildItem -Path "\\networkfolder\RawData\2_ActionData_Prep\" -Directory -Recurse
foreach ($folder in $CustomerFolders) {Get-ChildItem $folder -Filter "*.rpt" -File | Sort-Object -Property LastWriteTime -Descending |
Select-Object -Skip 1 |
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"}
Now I get an error message:
Get-ChildItem : Cannot find path '\networkfolder\CustomerA' because it does not exist.
It somehow misses the part \RawData\2_ActionData_Prep\ in the path, although I defined it in the $CustomerFolders variable?
You could do the process all with pipelines like this:
$base = "\\networkfolder\RawData\2_ActionData_Prep\"
$destination = "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"
Get-ChildItem -Path $base -Directory -Recurse | ForEach-Object {
$_ | Get-ChildItem -Filter "*.rpt" -File | Sort-Object LastWriteTime -Descending |
Select-Object -Skip 1 | Move-Item -Force -Destination $destination
}
To briefly explain why Get-ChildItem $folder... failed but $folder | Get-ChildItem ... worked, when we do Get-ChildItem $folder, $folder is being passed as argument for the -Path parameter and the parameter type for it is [string[]]. So, in your code when $folder (a DirectoryInfo instance) is passed as argument, it is being converted to a string and, very unfortunately in Windows PowerShell, when we type convert a DirectoryInfo (and a FileInfo too!) object to string what we get as a result is the Directory Name (this is not the case in PowerShell Core, where the string representation of this object becomes the Directory FullName (a.k.a. Absolute Path) so Get-ChildItem thinks it's being fed a relative path and it looking for the folders in your current location.
However when we do $folder | Get-ChildItem ..., $folder gets bound to the -LiteralPath parameter by Property Name on the PSPath ETS property, in other words, the cmdlet receives the object's provider path (you can think of it as the absolute path of the folder) hence why it works fine.

Getting original object after piping via Out-GridView

I need to show a list of file names without paths and open the selected file.
I can get it to work with full paths:
Get-ChildItem *.txt -Recurse | Sort-Object Name| Out-GridView -PassThru | Invoke-Item
But when I try to show only the file names it fails:
Get-ChildItem *.txt -Recurse | Sort-Object Name| Select-Object Name | Out-GridView -PassThru | Invoke-Item
By piping it through Get-Member I understand that Select-Object Name striped all non-Name properties. So how can I trace the original file object from what I got from GridView?
You might want to use the DefaultDisplayPropertySet property of the hidden PSStandardMembers set for this:
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]#('Name'))
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]#($defaultDisplayPropertySet)
Get-ChildItem *.txt -Recurse | Select-Object * |
ForEach-Object {
$_ | Add-Member MemberSet PSStandardMembers $PSStandardMembers; $_
} | Out-Gridview -PassThru | Select-Object FullName
Get-ChildItem *.txt has a default display propery set of: LastWriteTime, Length, Name
Select-Object * strips off the complete property set (displays all properties)
Add-Member MemberSet PSStandardMembers $PSStandardMembers adds a new display set with just the Name property and keeps the rest of the properties hidden
Select-Object FullName reveals the hidden FullName property
The problem is, that the Invoke-Item needs the path and not only the filename.
You could store the get-childitem in a temporary variable:
$tmp = Get-ChildItem *.txt -Recurse | Sort-Object Name
$tmp | Select-Object Name | Out-GridView -PassThru
$tmp | Invoke-Item
Is that what you wanted? Please let me know if it worked, and if it did please mark my post as the answer. :)

Get Subtypes of VersionInfo

I would like to get few file attributes from Powershell command Get-Item or Get-ItemProperty
Get-Item -Path c:\windows\system32\gdi32.dll | Select Name, Length, VersionInfo.ProductVersion, VersionInfo.FileVersion, CreationTime, LastAccessTime, LastWriteTime
Get-ItemProperty -Path c:\windows\system32\gdi32.dll -Name Name, Length, VersionInfo.ProductVersion, CreationTime, LastAccessTime, LastWriteTime
Both of the commands does not give me VersionInfo.ProductVersion
When you pass VersionInfo.ProductVersion as a parameter argument to a function, PowerShell interprets it as the string "VersionInfo.ProductVersion", and starts looking for a property with that exact name. But a FileInfo object doesn't have such a property, which is why it doesn't work.
You'll need a calculated property in order to grab the the property values of VersionInfo:
Get-Item ... |Select Name,Length,#{Name='ProductVersion';Expression={$_.VersionInfo.ProductVersion}},#{Name='FileVersion';Expression={$_.VersionInfo.FileVersion}},CreationTime,LastAccessTime,LastWriteTime
You can also prepare all the property names as an array ahead of time:
$ItemProperties = #(
'Name'
'Length'
#{Name = 'ProductVersion'; Expression = {$_.VersionInfo.ProductVersion}}
#{Name = 'FileVersion'; Expression = {$_.VersionInfo.FileVersion}}
'CreationTime'
'LastAccessTime'
'LastWriteTime'
)
Get-Item ... |Select $ItemProperties

Remove-Item cmdlet causes "Cannot find path" while process of removal of .exe files in local folder

I have script that selects .exe files with the specified name from the local folder and removes all files, except first.
$P variable is defined in param.
$P ="$($env:USERPROFILE)\Desktop\I"
Then I got this error
$C = Get-ChildItem $P -Filter *.exe| Where-Object Name -Like '*r_2-2*' | Sort-Object Name -Descending | Select-Object -ExpandProperty Name -Skip 1 | Remove-Item
Remove-Item : Cannot find path 'D:\FM\r_2-2.exe' because it does not exist.
At line:1 char:251
+ ... Descending | Select-Object -ExpandProperty Name -Skip 1 | Remove-Item
I know about foreach loop but want to use For-EachObject cmdlet instead.
You were quite close, if you want to use ForEach-Object:
Get-ChildItem $P -Filter *.exe | Where-Object Name -Like '*r_2-2*' | Select-Object -Skip 1 | ForEach-Object { remove-item $_.FullName -force }
To skip one first found result just Select-Object -Skip 1 is enough.
Remove-Item -Force also removes hidden and read-only files.
You can make the use of FullName parameter directly in your statement. Try this -
$C = Get-ChildItem $P -Filter *.exe| Where-Object Name -Like '*r_2-2*' | Sort-Object Name -Descending | Select-Object -ExpandProperty FullName -Skip 1
$c | ForEach-Object {Remove-Item -Path $_}
Use -Force parameter if you want to delete the hidden files too.

Sum of file folder size based on file/folder name

I have multiple folders across a number of SQL Servers that contain hundreds/thousands of databases. Each database comprises of three elements:
<dbname>.MDF
<dbname>.LDF
<dbname>files (Folder that contains db files/attachments)
I need to marry these files together and add up their total size, does anyone have any advice on how to do this?
EDIT : Just to clarify, I'm currently able to output the filesizes of the MDF/LDF files, I have a separate script that summarises the folder sizes. I need a method of adding together a .MDF/.LDF/DBFiles folder when their name matches. Bearing in mind all of the files are prefixed with the database name.
EDIT #2: The 2 options given so far sum together the .mdf/.ldf files with no problem, but do not add the folder size of the DBFiles folder. Does anyone have any input on how to amend these scripts to include a folder beginning with the same name.
First provided script:
$root = 'C:\db\folder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = Get-ChildItem "$root\$_*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum
}
}
Second provided script:
gci "c:\temp" -file -Include "*.mdf", "*.ldf" -Recurse |
group BaseName, DirectoryName |
%{new-object psobject -Property #{FilesAndPath=$_.Name; Size=($_.Group | gci | Measure-Object Length -Sum).Sum } }
EDIT #3:
Thanks to Ansgar (below), the updated solution has done the trick perfectly. Updating question with final solution:
$root = 'C:\db\folder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum
}
}
Enumerate just the .mdf files from your database folder, then enumerate the files and folders for each basename.
$root = 'C:\db\folder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum
}
}
if you want the sum of sise files database by dir and name file (without extension), try it
gci "c:\temp" -file -Include "*.mdf", "*.ldf" -Recurse |
group BaseName, DirectoryName |
%{new-object psobject -Property #{FilesAndPath=$_.Name; Size=($_.Group | gci | Measure-Object Length -Sum).Sum } }
Modifiy a little the include gci if necessary