Powershell 忽略文件中的第一行和最后一行
我正在尝试使用PowerShell替换多个文本文件的某些列中的字符。我有它工作得很好,除了我需要忽略每个文件中的第一行和最后一行,我不能让它工作 这就是我到目前为止所做的:Powershell 忽略文件中的第一行和最后一行,powershell,Powershell,我正在尝试使用PowerShell替换多个文本文件的某些列中的字符。我有它工作得很好,除了我需要忽略每个文件中的第一行和最后一行,我不能让它工作 这就是我到目前为止所做的: $Location = "C:\Users\gerhardl\Documents\Tenacity\TEMP\POWERSHELL TESTS" $Data = "$Location\*.TXT" $Output = "$Location\Fixed" Get-Item $Data | ForEach-Object
$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循环似乎不能很好地处理这些建议。跟踪第一行是可能的,可以对每个文件使用bool
$IsFirstLine=$True
,然后在ForEach对象内将其设置为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)
}
您可以使用
-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
我设法做到了以下几点-不完全是我发布的内容,但无法做到这一点。第一行和最后一行(标题和尾部记录)的长度要短得多,因此我做了以下工作:
$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)
}
注意:这篇文章回答了一个一般性问题,即如何从处理中排除输入文件/输入集合的第一行和最后一行
在PSv5+中工作得很好(假设输出中应消除第一行和最后一行)
但是,他们的PSv4-solution无法工作(截至本文撰写时),因为Get Content$file | Select Object-Skip 1
返回的数组([System.Object[]]
实例)没有.GetRange()
方法。下面是一个使用PowerShell的range操作符(
。
)的有效解决方案:
注:*尝试
[1..-1]
很诱人,但在PowerShell中不起作用,因为1..-1
的计算结果是下标1,0,-1
*如果您知道至少有3个输入对象,则可以省略
[Math]::Max()
调用
但是,上述解决方案并不总是一个选项,因为它要求首先收集内存中的所有输入对象,这否定了基于管道的解决方案提供的逐个内存限制处理(尽管内存中解决方案(如果可行)速度更快。) 要在PSv4-中解决此问题,您可以以管道友好的方式模拟
Select Object-SkipLast 1
,如下所示(Select Object-Skip 1
-从一开始跳过-在PSv4-)中受支持
每个输入对象的输出延迟一次迭代,这实际上忽略了最后一次迭代
下面是对-SkipLast
的泛化,作为高级函数跳过最后一个
实现,它使用实例延迟
对象的输出:
# 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)
}
}
}
你想删除第一行和最后一行还是保持不变?我可以像你一样提前修改答案以包括我的代码吗?我正在努力使这项工作,但不能得到正确的-我是一个完全的新手与PS和我的技能,包括恶毒的其他脚本一起。。。请记住,我有单独的文本文件,需要写入相同的单独文件名;但是我已经把你的代码放进去了,并且做了一些调整。现在正在写文件,但是似乎已经删除了所有的换行符。哦;将
$OutputLines=$Lines[0]+
转换为$OutputLines=@($Lines[0])+
v5解决方案很棒,但v4解决方案不起作用,因为$text
是一个数组([System.Object[]]
),而数组没有.GetRange()
方法。
# 'one', 'two', 'three' is a sample array. Output is 'one', 'two'
'one', 'two', 'three' | ForEach-Object { $notFirst = $False } {
if ($notFirst) { $prevObj }; $prevObj = $_; $notFirst = $True
}
# 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)
}
}
}
PS> 'one', 'two', 'three', 'four', 'five' | Skip-Last 3
one
two