【问题标题】:How to parse through folders and files using PowerShell?如何使用 PowerShell 解析文件夹和文件?
【发布时间】:2021-03-02 11:07:32
【问题描述】:

我正在尝试构建一个在特定文件夹和其中的日志文件中移动的脚本,并过滤错误代码。之后,它将它们传递到一个新文件中。

我不太确定如何使用 for 循环来做到这一点,所以我将在下面留下我的代码。

如果有人能告诉我我做错了什么,那将不胜感激。

$file_name = Read-Host -Prompt 'Name of the new file: '

$path = 'C:\Users\user\Power\log_script\logs'

Add-Type -AssemblyName System.IO.Compression.FileSystem
function Unzip
{
    param([string]$zipfile, [string]$outpath)

    [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
}

if ([System.IO.File]::Exists($path)) {
    Remove-Item $path
    Unzip 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'
} else {
    Unzip 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'
}

$folder = Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles'

$files = foreach($logfolder in $folder) {
    $content = foreach($line in $files) {
        if ($line -match '([ ][4-5][0-5][0-9][ ])') {
        echo $line
        }
    }
}


$content | Out-File $file_name -Force -Encoding ascii 

在 LogFiles 文件夹内还有另外三个文件夹,每个文件夹都包含日志文件。 谢谢

【问题讨论】:

  • 您应该研究的一件事是 Get-Childitem 的 -Recurse 参数。这将导致它搜索子文件夹。
  • 我试过了。不幸的是,它没有做它应该做的事情。
  • 它应该做什么?

标签: powershell logging


【解决方案1】:

扩展上面关于递归文件夹结构的评论,然后实际检索文件的内容,您可以尝试以下内容:

$allFiles = Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles' -Recurse

# iterate the files
$allFiles | ForEach-Object {
    # iterate the content of each file, line by line
    Get-Content $_ | ForEach-Object {
        if ($_ -match '([ ][4-5][0-5][0-9][ ])') {
            echo $_
        }
    }
}

【讨论】:

  • 你不需要外部的ForEach-Object,删除它之后你就​​不需要Get-Content命令中的$_了。 Get-Content 可以直接从Get-ChildItem 获取管道输入
【解决方案2】:

看起来您的内部循环是一个尚不存在的集合 ($files)。您将$files 分配给ForEach(...) 循环的输出,然后尝试在其中嵌套另一个$files 循环。当然此时$files 不能循环使用。

无论如何,问题是您从不阅读日志文件的内容。即使您设法循环了Get-ChildItem 的输出,您也需要查看每一行来执行匹配。

显然我无法完全测试这一点,但我发现了一些问题并重写如下:

$file_name = Read-Host -Prompt 'Name of the new file'
$path      = 'C:\Users\user\Power\log_script\logs'
$Pattern   = '([ ][4-5][0-5][0-9][ ])'

if ( [System.IO.File]::Exists( $path ) ) { Remove-Item $path }

Expand-Archive 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'

Select-String -Path 'C:\Users\user\Power\log_script\logs\LogFiles\*' -Pattern $Pattern |
Select-Object -ExpandProperty line |
Out-File $file_name -Force -Encoding ascii

注意:Select-String 不能自行递归。

我不确定您是否需要编写自己的 UnZip 函数。 PowerShell 有 Expand-Archive cmdlet,它至少可以匹配迄今为止的功能:

Expand-Archive -Path <SourceZipPath> -DestinationPath <DestinationFolder>

注意:-Force 参数允许它覆盖已经存在的目标文件。这可以替代测试文件是否存在,如果存在则删除。

如果你要测试文件,那段代码可以简化为:

if ( [System.IO.File]::Exists( $path ) ) { Remove-Item $path }

Unzip 'C:\Users\user\Power\log_script\logs.zip' 'C:\Users\user\Power\log_script'

这是因为您无论如何都要运行UnZip 命令...

注意:您也可以为此使用Test-Path

还有很多获取匹配行的方法,这里有几个额外的示例:

Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles' |
ForEach-Object{
    ( Get-Content $_.FullName ) -match $Pattern
    # Using match in this way will echo the lines that matched from each run of
    # Get-Content.  If nothing matched nothing will output on that iteration.
} |
Out-File $file_name -Force -Encoding ascii

这种方法会在运行匹配之前将整个文件读入一个数组。对于大文件,它可能会造成内存问题,但它启用了 -match 的巧妙使用。

或者:

Get-ChildItem -Path 'C:\Users\user\Power\log_script\logs\LogFiles' |
Get-Content |
ForEach-Object{ If( $_ -match $Pattern ) { $_ } } |
Out-File $file_name -Force -Encoding ascii

注意:您不需要别名 echo 或其真正的 cmdlet Write-Output

【讨论】:

    【解决方案3】:

    更新:在摸索了一下并尝试了不同的东西之后,我终于让它工作了。

    我将包含下面的代码仅用于演示目的。

    谢谢大家

    $start = Get-Date
    "`n$start`n"
    
    $file_name = Read-Host -Prompt 'Name of the new file: '
    
    Out-File $file_name -Force -Encoding ascii 
    
    Expand-Archive -Path 'C:\Users\User\Power\log_script\logs.zip' -Force
    $i = 1 
    $folders = Get-ChildItem -Path 'C:\Users\User\Power\log_script\logs\logs\LogFiles' -Name -Recurse -Include *.log
    
    foreach($item in $folders) {
        $files = 'C:\Users\User\Power\log_script\logs\logs\LogFiles\' + $item
        foreach($file in $files){
            $content = Get-Content $file
            Write-Progress -Activity "Filtering..." -Status "File $i of $($folders.Count)" -PercentComplete (($i / $folders.Count) * 100)
            $i++ 
            $output = foreach($line in $content) {
                if ($line -match '([ ][4-5][0-5][0-9][ ])') {
                Add-Content -Path $file_name -Value $line
                }
            }
        }
    }
    
    $end = Get-Date
    $time = [int]($end - $start).TotalSeconds
    
    Write-Output ("Runtime: " + $time + " Seconds" -join ' ')
    

    【讨论】:

      猜你喜欢
      • 2021-01-02
      • 2020-12-30
      • 2020-12-26
      • 1970-01-01
      • 2019-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多