[英]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.