Search code examples
powershellpowershell-3.0powershell-remoting

How to convert a log file that is an RTF format to plain text


I need to convert my log file that is generated from by backup solution app from RTF format to plain text continuously (as in make a whole new log that is an exact copy of that one that will always be an updated copy) in order to parse the log file for errors using a code I kindly got from another user (please find it below). Any Idea how to do this with code using powershell?

The code I have that can search a text file:

$daysBack = 3
$refDate  = (Get-Date).AddDays(-$daysBack).Date  # set this to midnight
$log      = Get-Content -Path 'C:\Users\<User>\Documents\TheLog.log'

# find lines that start with what looks like a date and contains 'Errors:'
# capture the date part in backreference $matches[1] to parse into a real datetime object for comparison with $refDate
$errors = @($log | Where-Object { $_ -match '(\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}).*Errors:' } |   
                   Where-Object { [datetime]::ParseExact($matches[1], 'dd/MM/yyyy HH:mm:ss', $null) -ge $refDate }).Count

# if $errors not 0
if ($errors) {
    $count = if ($errors -eq 1) { "was an error" } else { "were $errors errors" }
    "There {0} in your back up solution in the last $daysBack days. Please check your log file." -f $count
}
else {
    "There were no errors in backing up your files in the last $daysBack days."
}

I have tested with a plain text file version of my log file below:

18/02/2021 08:57:37 - can not access C:\users\<username>\documents\ The network path was not found.
18/02/2021 08:57:37 - End: Documents... copied: 0, Errors: 1
21/02/2021 08:57:37 - can not access C:\users\<username>\documents\ The network path was not found.
21/02/2021 08:57:37 - End: Documents... copied: 0, Errors: 1
22/02/2021 17:27:33 - Begin: Documents=======================================
22/02/2021 17:27:33 - copied Notes.docx from C:\users\<username>\documents\ to D:\users\<username>\documents\
22/02/2021 17:27:33 - End: Documents...Copied 1
22/02/2021 17:27:33 - End: Documents...Copied 1, Deleted: 1

The above code does not work for RTF. Any help or any idea how to change the above code to fit my log file in RTF format?

USING THE BELOW CODE BY @THEO :)

Cannot invoke method. Method invocation is supported only on core types in this language mode.

At line:41 char:13

+             $word.Quit()

+             ~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : MethodInvocationNotSupportedInConstrainedLanguage

 

Cannot invoke method. Method invocation is supported only on core types in this language mode.

At line:42 char:13

+             $null = [System.Runtime.InteropServices.Marshal]::Release ...

+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : MethodInvocationNotSupportedInConstrainedLanguage

 

Cannot invoke method. Method invocation is supported only on core types in this language mode.

At line:43 char:13

+             $null = [System.Runtime.InteropServices.Marshal]::Release ...

+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : MethodInvocationNotSupportedInConstrainedLanguage

 

Cannot invoke method. Method invocation is supported only on core types in this language mode.

At line:44 char:13

+             [System.GC]::Collect()

+             ~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : MethodInvocationNotSupportedInConstrainedLanguage

 

Cannot invoke method. Method invocation is supported only on core types in this language mode.

At line:45 char:13

+             [System.GC]::WaitForPendingFinalizers()

+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : MethodInvocationNotSupportedInConstrainedLanguage

 

Cannot set property. Property setting is supported only on core types in this language mode.

At line:17 char:9

+         $word.Visible = $false

+         ~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : PropertySetterNotSupportedInConstrainedLanguage

 

Get-Content : Cannot bind argument to parameter 'Path' because it is null.

At line:55 char:31

+ $log      = Get-Content -Path $plainTextLog

+                               ~~~~~~~~~~~~~

    + CategoryInfo          : InvalidData: (:) [Get-Content], ParameterBindingValidationException

    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentC

   ommand

Solution

  • To convert from RTF to plain-text file, you can use the following helper function:

    function ConvertFrom-RTF {
      [CmdletBinding()]
        Param(
            [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
            [Alias('FullName')]
            [string]$Path
        )
    
        # this is the output format
        # see:  https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.word.wdsaveformat?view=word-pia
        $WdSaveFormat = 3   # wdFormatTextLineBreaks
    
        try {
            $word = New-Object -ComObject Word.Application
            $word.Visible = $false
            $doc = $word.Documents.Open($Path)
            # Replace the source file extenson with the appropriate target file extension 
            $fileOut = [System.IO.Path]::ChangeExtension($Path, '.txt')
    
            if (Test-Path -Path $fileOut -PathType Leaf) {
                # Delete existing file
                Remove-Item -Path $fileOut -Force -Confirm:$false
            }
            # Check Version of Office Installed. Pre 2010 versions need [ref] on the parameters
            if ($word.Version -gt '14.0') {
                $doc.SaveAs($fileOut, $WdSaveFormat)
            }
            else {
                $doc.SaveAs([ref]$fileOut,[ref]$WdSaveFormat)
            }
            $doc.Close($false)
    
            # return the filename of the converted file
            return $fileOut
        }
        finally {
            # cleanup code
            if ($word) {
                $word.Quit()
                $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($doc)
                $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($word)
                [System.GC]::Collect()
                [System.GC]::WaitForPendingFinalizers()
            }
        }
    }
    
    # the returned value is the full path and filename of the converted text file
    $plainTextLog = ConvertFrom-RTF -Path 'C:\Users\<User>\Documents\TheLog.rtf'
    

    Below that, add the code you already have from here to parse the log:

    $daysBack = 3
    $refDate  = (Get-Date).AddDays(-$daysBack).Date  # set this to midnight
    $log      = Get-Content -Path $plainTextLog
    
    # find lines that start with what looks like a date and contains 'Errors:'
    # capture the date part in backreference $matches[1] to parse into a real datetime object for comparison with $refDate
    $errors = @($log | Where-Object { $_ -match '(\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}).*Errors:' } |   
                       Where-Object { [datetime]::ParseExact($matches[1], 'dd/MM/yyyy HH:mm:ss', $null) -ge $refDate }).Count
    
    # if $errors not 0
    if ($errors) {
        $count = if ($errors -eq 1) { "was an error" } else { "were $errors errors" }
        "There {0} in your back up solution in the last $daysBack days. Please check your log file." -f $count
    }
    else {
        "There were no errors in backing up your files in the last $daysBack days."
    }