Powershell:上次运行后从文件复制新条目

问题描述 投票:0回答:2

我正在尝试创建一个 powershell 脚本,以便在每次运行脚本时仅将与模式(可以是一行或多行)匹配的新条目从一个文件复制到另一个文件。源文件由应用程序随机更新,但请求是每小时复制最后的条目。

我正在研究的解决方案是获取文件中存储的上一次运行的最后一个条目,并与文件中的最后一个条目进行比较,如果它们不匹配,则开始复制该行之后的新行。这就是我被卡住的部分,我不知道如何表示,而是每次都复制整个内容。

这是我到目前为止得到的:

Write-Host "Declaring the output log file ..... " -ForegroundColor Yellow
$destinationfile = 'C:\file\out\output.log'


Write-Host "Getting the last line of the source file ..... " -ForegroundColor Yellow
$sourcefile = 'C:\app\blade\inputlogs.log'
$sourcefilelastline = Get-Content $originfile | Select-Object -last 1
$sourcefilelastline


Write-Host "Getting the last line of the destination file ..... " -ForegroundColor Yellow
$destinationfilelastline = Get-Content $destinationfile | Select-Object -last 1
$destinationfilelastline


if ($sourcefilelastline -eq $destinationfilelastline){
    Write-Host "Skipping the process ..... " -ForegroundColor Yellow
}
else{
    Write-Host "Reading the source log file and updating destination file  ..... " -ForegroundColor Yellow
    $sourcefilecontent = Get-Content -Path $sourcefile | Where-Object { $_ -ne '' } | Select-String -Pattern 'error' -CaseSensitive -SimpleMatch
    $sourcefilecontent | Add-Content $destinationfile
}

关于如何完成这件事有什么想法吗?谢谢。

powershell logfile
2个回答
0
投票

这是一个小小的实验,但似乎效果很好。

Read-LastLinesOfTextFile 函数:

  1. 接受 $AppName 参数,该参数用于定义注册表中存储值的路径 - 在本例中为上次读取文件结束的位置。
    • 这里的想法是,您可以有多个脚本调用此函数,但因为每个脚本使用唯一的名称,所以它们之间不会有任何冲突。
  2. 接受 $TextFile 参数,该参数是需要读取的文件的路径。
  3. 返回以字符串数组形式读取的行。
  4. 使用seek设置文件流的读取位置,用于读取文件。
  5. 从文件流读取后,检索新位置并将其保存到注册表,下次调用该函数时将使用该位置。
function Read-LastLinesOfTextFile {
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$AppName,
        [Parameter(Mandatory = $true, Position = 1)]
        [string]$TextFile
    )
    $RegistryPath = "HKCU:\Software\$AppName"               # Registry key path for storing registry value
    if (-not (Test-Path "$RegistryPath")) {                 # If key path does not exists
        $null = New-Item -Path $RegistryPath -Force         #   Then create registry key
    }
    # Check if the registry value for LastPosition does NOT exists - https://stackoverflow.com/a/43921551/4190564
    if ( (Get-ItemProperty "$RegistryPath").PSObject.Properties.Name -notcontains "LastPosition" ) {
        # Create LastPosition value
        $null = New-ItemProperty -Path $RegistryPath -Name "LastPosition" -Value 0 -PropertyType DWORD -Force
    }
    # Save registry value LastPosition to $LastPosition
    $LastPosition = Get-ItemPropertyValue -Path $RegistryPath -Name "LastPosition"
    $CurrentFileSize = (Get-Item $TextFile).Length          # Get current file size
    if ($CurrentFileSize -lt $LastPosition) {               # If the file is smaller than it used to be
        $LastPosition = 0                                   #   Then assume it was deleted and now has all new data.
    } elseif ($CurrentFileSize -lt $LastPosition) {
        return @()
    }
    # Open file stream
    try { $FileStream = New-Object System.IO.FileStream($TextFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) }
    catch { return @() }
    # Open stream reader
    try { $StreamReader = New-Object System.IO.StreamReader($FileStream) }
    catch {
        $FileStream.Close()                                 # Close FileStream
        return @()
    }
    $Return = @()                                           # Define default return value of empty array
    try {
        if ($LastPosition) {                                # If LastPosition anything other than 0
            # Seek last position
            $null = $FileStream.Seek($LastPosition, [System.IO.SeekOrigin]::Begin)
        }
        $Text = $StreamReader.ReadToEnd()                   # Read to the end of the file
        # Update the registry with the new last position
        Set-ItemProperty -Path $RegistryPath -Name "LastPosition" -Value $FileStream.Position
        $Return = $Text -split "\r?\n"                      # Split string into lines. https://stackoverflow.com/a/76831908/4190564
    }
    finally {
        $StreamReader.Close()                               # Close StreamReader
        $FileStream.Close()                                 # Close FileStream
    }
    return $Return
}

要调用此函数,请使用类似于以下内容的行,只需确保将

MyLogReader
替换为您的脚本特有的名称,这样您就不会与使用相同函数的其他脚本发生冲突。看起来您正在与“错误”进行区分大小写的比较,因此我在本示例中使用了
-cmatch
。如果您希望不区分大小写,请将其更改为
-match

Read-LastLinesOfTextFile 'MyLogReader' "$PSScriptRoot\MyLogFile.LOG" | Where-Object { $_ -cmatch 'error' } | Add-Content $destinationfile

0
投票

Get-content 有一个开关“tail”,可让您从文件中读取最后几行。根据微软自己的话:

Tail 参数获取文件的最后一行。此方法比检索变量中的所有行并使用 [-1] 索引表示法更快。

您可以在您的案例中从底线开始使用它,直到它们匹配为止。

function Get-FileLastContent {
    param (
        [Parameter(
            Position = 0,
            Mandatory = $true,
            HelpMessage = 'Path to File')]
        [ValidateScript({ Test-Path -Path $_ })]
        [string]
        $FilePath,

        [Parameter(
            Position = 1,
            HelpMessage = 'Number of lines lines to check')]
        [ValidateNotNullOrEmpty()]
        [int]
        $LastLines = 1
    )



        $FileContent = Get-Content -Path $FilePath -Tail $LastLines
        Return $FileContent 
}


Write-Host -Object  "Declaring the output log file ..... " -ForegroundColor Yellow
$destinationfile = 'C:\file\out\output.log'

Write-Host -Object "Getting the last line of the destination file ..... " -ForegroundColor Yellow
$destinationfilelastline = Get-FileLastContent -FilePath $destinationfile
$destinationfilelastline


$LastLines = 1
Write-Host -Object "Getting the last line of the source file ..... " -ForegroundColor Yellow
$sourcefile = 'C:\app\blade\inputlogs.log'
$sourcefilelastline = Get-FileLastContent -FilePath $sourcefile -LastLines $LastLines
Write-Host -Object $sourcefilelastline

if (($sourcefilelastline -ne $destinationfilelastline)) {
    Write-Host -Object "Reading the source log file and updating destination file  ..... " -ForegroundColor Yellow
    while ($sourcefilelastline[0] -ne $destinationfilelastline) {
        $LastLines=$LastLines+1
        $sourcefilelastline = Get-FileLastContent -FilePath $sourcefile -LastLines $LastLines
    }
    #$sourcefilelastline =  $sourcefilelastline | Where-Object { $_ -ne '' } | Select-String -Pattern 'error' -CaseSensitive -SimpleMatch
    $sourcefilelastline= $sourcefilelastline | Select-Object -Skip 1
    @("`n") + $sourcefilelastline | Add-Content -Path $destinationfile -Force -Encoding UTF8 -NoNewline
}
else {
    Write-Host -Object "Skipping the process ..... " -ForegroundColor Yellow
}
© www.soinside.com 2019 - 2024. All rights reserved.