Reputation: 41
I have this working, but need LastWriteTime
and can't get it.
Get-ChildItem -Recurse | Select-String -Pattern "CYCLE" | Select-Object Path, Line, LastWriteTime
I get an empty column and zero Date-Time data
Upvotes: 4
Views: 1725
Reputation: 1
In PowerShell, I was able to run the following line:
Get-ChildItem -Path $logPath |? {!$_.PSIsContainer -and $_.Extension -eq ".log"} | Foreach{$_.Refresh()}
Before the line I wanted to use the LastWriteTime
in:
Get-ChildItem -Path $logPath | ? {!$_.PSIsContainer -and $_.Extension -eq ".log" `
-and $_.LastWriteTime -gt (Get-Date).AddSeconds(-35)} `
| Sort-Object -Property Name -Descending | Select-Object -First 1
Upvotes: 0
Reputation: 21418
LastWriteTime
is a property of System.IO.FileSystemInfo, which is the base type of the items Get-ChildItem
returns for the Filesystem
provider (which is System.IO.FileInfo for files). Path
and Line
are properties of Microsoft.PowerShell.Commands.MatchInfo
, which contains information about the match, not the file you passed in. Select-Object
operates on the information piped into it, which comes from the previous expression in the pipeline, your Select-String
in this case.
You can't do this as a (well-written) one-liner if you want the file name, line match, and the last write time of the actual file to be returned. I recommend using an intermediary PSCustomObject
for this and we can loop over the found files and matches individually:
# Use -File to only get file objects
$foundMatchesInFiles = Get-ChildItem -Recurse -File | ForEach-Object {
# Assign $PSItem/$_ to $file since we will need it in the second loop
$file = $_
# Run Select-String on each found file
$file | Select-String -Pattern CYCLE | ForEach-Object {
[PSCustomObject]@{
Path = $_.Path
Line = $_.Line
FileLastWriteTime = $file.LastWriteTime
}
}
}
Note: I used a slightly altered name of
FileLastWriteTime
to exemplify that this comes from the returned file and not the match provided bySelect-String
, but you could useLastWriteTime
if you wish to retain the original property name.
Now $foundMatchesInFiles
will be a collection of files which have CYCLE
occurring within them, the path of the file itself (as returned by Select-String
), and the last write time of the file itself as was returned by the initial Get-ChildItem
.
You could also use Select-Object
and computed properties but IMO the above is a more concise approach when merging properties from unrelated objects together. While not a poor approach, Select-Object
outputs data with a type containing the original object type name (e.g. Selected.Microsoft.PowerShell.Commands.MatchInfo
). The code may work fine but can cause some confusion when others who may consume this object in the future inspect the output members. LastWriteTime
, for example, belongs to FileSystemInfo
, not MatchInfo
. Another developer may not understand where the property came from at first if it has the MatchInfo
type referenced. It is generally a better design to create a new object with the merged properties.
That said this is a minor issue which largely comes down to stylistic preference and whether this object might be consumed by others aside from you. I write modules and scripts that many other teams in my organization consume so this is a concern for me. It may not be for you. @mklement0's answer is an excellent example of how to use computed properties with Select-Object
to achieve the same functional result as this answer.
Upvotes: 3
Reputation: 437218
Select-String
's output objects, which are of type Microsoft.PowerShell.Commands.MatchInfo
, only contain the input file path (string), no other metadata such as LastWriteTime
.
To obtain it, use a calculated property, combined with the common -PipelineVariable
parameter,
which allows you to reference the input file at hand in the calculated property's expression script block as a System.IO.FileInfo
instance as output by Get-ChildItem
, whose .LastWriteTime
property value you can return:
Get-ChildItem -File -Recurse -PipelineVariable file |
Select-String -Pattern "CYCLE" |
Select-Object Path,
Line,
@{
Name='LastWriteTime';
Expression={ $file.LastWriteTime }
}
Note how the pipeline variable, $file
, must be passed without the leading $
(i.e. as file
) as the -PipelineVariable
argument . -PipelineVariable
can be abbreviated to -pv
.
Upvotes: 4