我正在尝试使用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循环不适合这些建议。
答案 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