繁体   English   中英

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

[英]Ignore first and last line in file

我正在尝试使用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循环不能很好地配合这些建议。

您可以使用-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

对于每个文件$IsFirstLine = $True ,可以通过bool跟踪第一行,然后在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)

}

我设法做到了这一点-不完全是我发布的内容,但无法完成该工作。 第一行和最后一行(标题和尾部记录)的长度短得多,所以我做了以下工作:

$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)

    }

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

Manu的帮助... | Select-Object -Skip 1 | Select-Object -SkipLast 1 ... | Select-Object -Skip 1 | Select-Object -SkipLast 1 ... | Select-Object -Skip 1 | Select-Object -SkipLast 1解决方案PSv5 +中效果很好(假设应该从输出中删除第一行和最后一行)。

但是,他们的PSv4解决方案不起作用(在撰写本文时),因为Get-Content $file | Select-Object -Skip 1返回的数组( [System.Object[]]实例) Get-Content $file | Select-Object -Skip 1 Get-Content $file | Select-Object -Skip 1没有.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[]]实例延迟<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代替[int]::MaxValue ,因为在这种情况下PSv2仅支持常量

样品电话:

PS> 'one', 'two', 'three', 'four', 'five' | Skip-Last 3
one
two

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM