忽略文件中的第一行和最后一行

时间:2017-06-13 17:13:59

标签: powershell

我正在尝试使用PowerShell替换多个文本文件的某些列中的字符。除非我需要忽略每个文件中的第一行和最后一行,否则我无法正常工作。

这是我到目前为止所做的:

$Location = "C:\Users\gerhardl\Documents\Tenacity\TEMP\POWERSHELL TESTS"
$Data = "$Location\*.TXT"
$Output = "$Location\Fixed"

Get-Item $Data |
    ForEach-Object {
        $file = $_
        $_ | 
            Get-Content | 
            ForEach-Object {
                $Beginning = $_.Substring(0,105)
                $Account = $_.Substring(105,20) -replace "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", " "
                $End = $_.Substring(125)
                '{0}{1}{2}' -f $Beginning,$Account,$End
            } |
            Set-Content -Path (Join-Path $Output  $file.Name)

    }

我知道有类似的线程,但似乎我的For Each循环不适合这些建议。

4 个答案:

答案 0 :(得分:4)

您可以使用-Skip 1-SkipLast 1

Get-Content $file  | Select-Object -Skip 1 | Select-Object -SkipLast 1

编辑PS< 5:

$text = Get-Content $file | Select-Object -Skip 1
$newText = $text.GetRange(0,($text.Count - 1))
$newText

答案 1 :(得分:2)

可以使用bool为每个文件$IsFirstLine = $True跟踪第一行,然后在ForEach-Object中将其设置为false。但我认为,跟踪最后一行是不可能的,你的管道方法 - 你已经处理了最后一行,然后才知道它是最后一行。

因此,您需要另一个循环来计算行数或缓冲区,以便在识别出最后一行后撤消更改。

如果文件足够小以读入内存,也许您可​​以使用以下方法:

$Location = "C:\Users\gerhardl\Documents\Tenacity\TEMP\POWERSHELL TESTS"
$Data = "$Location\*.TXT"
$Output = "$Location\Fixed"

Get-Item $Data | ForEach-Object {                   # for each file..

    $Lines = @(Get-Content $_.FullName)             # read all the lines, force array.
    $LinesToProcess = $Lines[1..($Lines.Count - 1)] # get lines except first and last.

    $ProcessedLines = $LinesToProcess | ForEach-Object {    # for each line..

        $Beginning = $_.Substring(0,105)
        $Account = $_.Substring(105,20) -replace "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", " "
        $End = $_.Substring(125)
        '{0}{1}{2}' -f $Beginning,$Account,$End

    }

    $OutputLines = $Lines[0] + $ProcessedLines + $Lines[-1] # add original first and last

    $OutputLines | Set-Content -Path (Join-Path $Output $_.Name)

}

答案 2 :(得分:0)

我设法按照以下方式执行此操作 - 不完全是我发布但无法完成的工作。第一行和最后一行(标题和预告片记录)的长度要短得多,所以我做了以下内容:

$Location = "C:\Users\gerhardl\Documents\Tenacity\TEMP\POWERSHELL TESTS"
$Data = "$Location\*.TXT"
$Output = "$Location\Fixed"

Get-Item $Data |
    ForEach-Object {
        $file = $_
        $_ | 
            Get-Content | 
            ForEach-Object {
            if ($_.length -gt 30)
            { 

                $Beginning = $_.Substring(0,105)
                $Account = $_.Substring(105,20) -replace "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", " "
                $End = $_.Substring(125)
                '{0}{1}{2}' -f $Beginning,$Account,$End
            }
            ELSE {
                $All = $_.Substring(0)
                '{0}' -f $All
                 }

            } |

            Set-Content -Path (Join-Path $Output  $file.Name)

    }

答案 3 :(得分:0)

注意:这篇文章回答了如何从处理中排除输入文件/输入集合的第一行和最后一行的一般性问题。

Manu's helpful ... | Select-Object -Skip 1 | Select-Object -SkipLast 1 solution PSv5 + 中效果很好(假设第一行和最后一行应该从输出消除)。

然而,他们的 PSv4-解决方案无法正常工作(截至撰写本文时),因为[System.Object[]]返回的数组(Get-Content $file | Select-Object -Skip 1实例)并不是&#39 ; t有一个.GetRange()方法 这是一个使用PowerShell范围运算符(..)的工作解决方案:

# Read lines of the input file into an array.
$allLines = Get-Content $file
# Using the range operator (..), get all elements except the first and the last.
$allLines[1..([Math]::Max(1, $allLines.Count-2))]

注意:
*尝试[1..-1]很有吸引力,但在PowerShell中工作,因为1..-1会评估下标1, 0, -1
*如果您知道至少有 3 输入对象,则可以省略[Math]::Max()来电。

然而,上述解决方案并不总是一个选项,因为需要首先收集内存中的所有输入对象 否定内存限制,基于管道的解决方案提供的逐一处理 (尽管内存中的解决方案,如果可行,更快。)

要在 PSv4 - 中解决此问题,您可以 管道方式模仿Select-Object -SkipLast 1 如下(Select-Object -Skip 1 - 跳过开始 - 支持PSv4 - )。

# 'one', 'two', 'three' is a sample array. Output is 'one', 'two'
'one', 'two', 'three' | ForEach-Object { $notFirst = $False } { 
  if ($notFirst) { $prevObj }; $prevObj = $_; $notFirst = $True
}

每个输入对象的输出延迟一次迭代,这实际上省略了最后一次。

这里的概括为-SkipLast <n>,实现为高级函数Skip-Last ,它使用[System.Collections.Generic.Queue[]]实例将输出延迟{{1对象:

<n>

注意:在上面的# Works in PSv2+ # In PSv5+, use `Select-Object -SkipLast <int>` instead. Function Skip-Last { <# .SYNOPSIS Skips the last N input objects provided. N defaults to 1. #> [CmdletBinding()] param( [ValidateRange(1, 2147483647)] [int] $Count = 1, [Parameter(Mandatory = $True, ValueFromPipeline = $True)]$InputObject ) begin { $mustEnumerate = -not $MyInvocation.ExpectingInput # collection supplied via argument $qeuedObjs = New-Object System.Collections.Generic.Queue[object] $Count } process { # Note: $InputObject is either a single pipeline input object or, if # the -InputObject *parameter* was used, the entire input collection. # In the pipeline case we treat each object individually; in the # parameter case we must enumerate the collection. foreach ($o in ((, $InputObject), $InputObject)[$mustEnumerate]) { if ($qeuedObjs.Count -eq $Count) { # Queue is full, output its 1st element. # The queue in essence delays output by $Count elements, which # means that the *last* $Count elements never get emitted. $qeuedObjs.Dequeue() } $qeuedObjs.Enqueue($o) } } } 属性中,使用ValidateRange()代替2147483647,因为在这种情况下PSv2仅支持常量。 / SUP>

示例电话:

[int]::MaxValue