Home > Software engineering >  PowerShell, can't get LastWriteTime
PowerShell, can't get LastWriteTime

Time:10-30

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

CodePudding user response:

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 by Select-String, but you could use LastWriteTime 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.


Technically speaking you could also get fancy with Select-Object and computed properties on the initial Get-ChildItem but IMO the above is a more readable and maintainable approach.

CodePudding user response:

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.

  • Related