2017-06-13 154 views
0

第一和最后一行我想在使用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循环不与这些建议发挥好。

+1

难道要删除的第一个和最后一行或让他们保持不变? – LotPings

回答

2

跟踪的第一行是可能的每个文件$IsFirstLine = $True一个bool,然后将其给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) 

} 
+0

我可以像您一样向前修改答案以包含我的代码吗?我正在努力完成这项工作,但无法做到这一点 - 我是一名PS老手,我的技能包括混杂在一起的其他脚本......请记住,我有单独的文本文件需要写出来到相同的单独文件名。 – user2725402

+0

我只勾勒出它,没有尝试过;但我已经把你的代码放入并做了一些调整。 – TessellatingHeckler

+0

现在写出这些文件,但似乎删除了所有换行符。 – user2725402

3

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

v5解决方案非常棒,但v4解决方案无法正常工作,因为$ text是一个数组('[System.Object []]'),而数组没有'.GetRange()'方法。 – mklement0

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) 

    } 
0

Manu's helpful ... | Select-Object -Skip 1 | Select-Object -SkipLast 1 solutionPSv5 +的伟大工程(假设第一和最后一行应该是从消除输出)。

然而,他们的PSv4-解决方案不起作用(本文写作时),因为Get-Content $file | Select-Object -Skip 1返回的数组([System.Object[]]实例)没有一个.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
*如果您知道至少有输入对象,则可以省略[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+ 
Function Skip-Last { 
    <# 
    .SYNOPSIS 
    Skips the last N input objects provided via the pipeline. 
    Default for N is 1. 
    #> 
    [CmdletBinding()] 
    param(
    [ValidateRange(1, 2147483647)] [int] $Count = 1, 
    [Parameter(Mandatory=$True, ValueFromPipeline=$True)]$InputObject 
) 
    begin { 
    if (-not $MyInvocation.ExpectingInput) { } 
    $qeuedObjs = New-Object System.Collections.Generic.Queue[object] $Count 
    } 
    process { 
    if ($qeuedObjs.Count -eq $Count) { 
     $qeuedObjs.Dequeue() 
    } 
    $qeuedObjs.Enqueue($InputObject) 
    } 
} 

注:在上面的ValidateRange()属性,2147483647来代替​​,因为在这种情况下PSv2仅支持常量

样品电话:

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