【问题标题】:How can I search the first line and the last line in a text file?如何搜索文本文件的第一行和最后一行?
【发布时间】:2013-01-18 15:52:48
【问题描述】:

我只需要搜索文本文件中的第一行和最后一行即可找到“-”并将其删除。 我该怎么做? 我试过选择字符串,但我不知道找到第一行和最后一行,只从那里删除“-”。

文本文件如下所示:

 % 01-A247M15 G70 
N0001 G30 G17 X-100 Y-100 Z0
N0002 G31 G90 X100 Y100 Z45
N0003 ; --PART NO.:  NC-HON.PHX01.COVER-SHOE.DET-1000.050 
N0004 ; --TOOL:  8.55 X .3937 
N0005 ;  
N0006  % 01-A247M15 G70 

这样的?

$1 = Get-Content C:\work\test\01.I

$1 | select-object -index 0, ($1.count-1)

【问题讨论】:

    标签: powershell powershell-ise


    【解决方案1】:

    好的,所以在看了一段时间之后,我决定必须有一种方法可以用一个衬里来做到这一点。这里是:

    (gc "c:\myfile.txt") | % -Begin {$test = (gc "c:\myfile.txt" | select -first 1 -last 1)} -Process {if ( $_ -eq $test[0] -or $_ -eq $test[-1] ) { $_ -replace "-" } else { $_ }} | Set-Content "c:\myfile.txt"
    

    以下是其作用的细分:

    首先,现在熟悉的别名。我只是把它们放进去,因为命令足够长,所以这有助于保持事情的可管理性:

    1. gc 表示 Get-Content
    2. % 表示 Foreach
    3. $_ 用于当前管道值(这不是别名,但我想我会定义它,因为你说你是新人)

    好的,现在是这里发生的事情:

    1. (gc "c:\myfile.txt") | --> 获取c:\myfile.txt 的内容并发送给下一行
    2. % --> 执行 foreach 循环(分别遍历管道中的每个项目)
    3. -Begin {$test = (gc "c:\myfile.txt" | select -first 1 -last 1)} --> 这是一个开始块,它在进入管道之前运行这里的所有内容。它将c:\myfile.txt 的第一行和最后一行加载到一个数组中,以便我们检查第一个和最后一个项目
    4. -Process {if ( $_ -eq $test[0] -or $_ -eq $test[-1] ) --> 这将对管道中的每个项目进行检查,检查它是文件中的第一项还是最后一项
    5. { $_ -replace "-" } else { $_ } --> 如果是第一个或最后一个,它会替换,如果不是,它就不管它
    6. | Set-Content "c:\myfile.txt" --> 这会将新值放回文件中。

    有关这些项目的更多信息,请访问以下网站:

    Get-Content uses
    Get-Content definition
    Foreach
    The Pipeline
    Begin and Process Foreach 的一部分(这通常用于自定义函数,但它们在 foreach 循环中作为好吧)
    If ... else声明
    Set-Content

    所以我在想,如果您想对许多文件执行此操作,或者想要经常执行此操作,该怎么办。我决定制作一个功能来满足您的要求。这是函数:

    function Replace-FirstLast {
        [CmdletBinding()]
        param(
            [Parameter( `
                Position=0, `
                Mandatory=$true)]
            [String]$File,
            [Parameter( `
                Position=1, `
                Mandatory=$true)]
            [ValidateNotNull()]
            [regex]$Regex,
            [Parameter( `
                position=2, `
                Mandatory=$false)]
            [string]$ReplaceWith=""
        )
    
    Begin {
        $lines = Get-Content $File
    } #end begin 
    
    Process {
        foreach ($line in $lines) {
            if ( $line -eq $lines[0]  ) {
                $lines[0] = $line -replace $Regex,$ReplaceWith 
            } #end if
            if ( $line -eq $lines[-1] ) {
                $lines[-1] = $line -replace $Regex,$ReplaceWith
            }
        } #end foreach
    }#End process
    
    end {
        $lines | Set-Content $File
    }#end end
    
    } #end function
    

    这将创建一个名为Replace-FirstLast 的命令。它会被这样调用:

    Replace-FirstLast -File "C:\myfiles.txt" -Regex "-" -ReplaceWith "NewText"
    

    -Replacewith 是可选的,如果它为空,它将被删除(默认值为"")。 -Regex 正在寻找一个正则表达式来匹配您的命令。有关将其放入您的个人资料的信息,请查看this article

    请注意:如果您的文件非常大(几 GB),这不是最佳解决方案。这会导致整个文件驻留在内存中,这可能会导致其他问题。

    【讨论】:

    • 很好的答案,但请注意,如果文件变得很大,最好在开始语句中使用get-content -First 1... -Last 1,这样在处理时就不会在内存中保留 xxGB 文件.
    • 是的,但是我们又回到了它没有将整个文件传回的问题,只是第一行和最后一行。
    • 没有。我在开始声明中说foreach -begin { ..here... }。 :)
    • Get-content 没有 -first-last 参数,至少在 PSv2 中是这样。
    • 它们实际上被称为-Totalcount(first) 和-Tail(last)。 first 和 last 是有效的别名(至少在 3.0 中):) 编辑:哦,tail 是在 3.0 中引入的,但无论如何这个问题都被标记了。
    【解决方案2】:

    尝试:

    $txt = get-content c:\myfile.txt
    $txt[0] = $txt[0] -replace '-'
    $txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-'
    $txt | set-content c:\myfile.txt
    

    【讨论】:

    • 错别字:$a --> $txt(最后一行)。 $txt[1] --> $txt[0] :-)
    • 另外,您可以调用$txt[-1] 来获取数组中的最后一项
    • 如果文件是一个巨大的文本文件怎么办?说 10GB 的文本文件。
    • 然后你将它与读取计数分开,并使用 add-content 将其部分保存为新文件。并在第一行和最后一行使用带有 if-tests 的长而巧妙的 foreach 循环。 :)
    【解决方案3】:

    您可以使用select-object cmdlet 来帮助您解决此问题,因为get-content 基本上将一个文本文件作为一个巨大的数组输出。

    因此,您可以这样做

    get-content "path_to_my_awesome_file" | select -first 1 -last 1
    

    之后要移除破折号,您可以使用-Replace 开关找到破折号并将其移除。这比使用System.String.Replace(...) 方法要好,因为它可以匹配正则表达式语句并替换整个字符串数组!

    看起来像:

    # gc = Get-Content. The parens tell Powershell to do whatever's inside of it 
    # then treat it like a variable.
    (gc "path_to_my_awesome_file" | select -first 1 -last 1) -Replace '-',''
    

    【讨论】:

    • 我也是这样想的,我发现唯一的问题是他需要整个数组。如果您尝试将其通过管道传输回文件,它将只有第一行和最后一行,而不是整个文件的第一行和最后一行都已更改。
    • 另外,它应该是gc 作为Get-Content 的别名。 gciGet-ChildItem 的别名
    • 感谢您的指正;我经常使用cat,有时我会忘记 cmdlet 的全名。 :)
    • 没问题,我会自己编辑它,但我没有代表只更改两个字母。
    【解决方案4】:

    如果您的文件非常大,您可能不想读取整个文件来获取最后一行。 gc -Tail 会很快为您获取最后一行。

    function GetFirstAndLastLine($path){
    
        return  New-Object PSObject -Property @{        
            First = Get-Content $path -TotalCount 1
            Last = Get-Content $path -Tail 1
            }
    }
    
    GetFirstAndLastLine "u_ex150417.log"
    

    我在一个 20 GB 的日志文件上尝试了这个,它立即返回。读取文件需要几个小时。

    如果您想保留所有删除的内容并且只想从最后删除,您仍然需要阅读该文件。使用 -Tail 是一种快速检查它是否存在的方法。

    我希望它有所帮助。

    【讨论】:

      【解决方案5】:

      对上述问题的更清晰的回答:

      $Line_number_were_on = 0
      $Awesome_file = Get-Content "path_to_ridiculously_excellent_file" | %{ 
          $Line = $_ 
          if ($Line_number_were_on -eq $Awesome_file.Length) 
               { $Line -Replace '-','' } 
          else 
               { $Line } ; 
          $Line_number_were_on++ 
      } 
      

      我喜欢单行文字,但我发现有时当我将简洁性置于功能之上时,可读性往往会受到影响。如果您正在做的事情将成为其他人将阅读/维护的脚本的一部分,则可能需要考虑可读性。

      【讨论】:

      • 此脚本存在一些问题。首先,它只适用于最后一行,而不是第一行和最后一行。其次,$line 永远不会放回任何东西,所以工作完成然后丢失,永远放回文件中。
      【解决方案6】:

      按照尼克的回答:我确实需要对目录树中的所有文本文件执行此操作,这就是我现在正在使用的:

      Get-ChildItem -Path "c:\work\test" -Filter *.i | where { !$_.PSIsContainer } | % { 
      $txt = Get-Content $_.FullName; 
      $txt[0] = $txt[0] -replace '-'; 
      $txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-';
      $txt | Set-Content $_.FullName
      }
      

      现在看起来运行良好。

      【讨论】:

        【解决方案7】:

        简单的过程: 将 $file.txt 替换为您的文件名

        获取内容 $file_txt |选择对象 -last 1

        【讨论】:

          【解决方案8】:

          我最近在 .bat 文件的最后一行中搜索 cmets。它似乎弄乱了以前命令的错误代码。我发现这对于在文件的最后一行中搜索模式很有用。 Pspath 是 get-content 输出的隐藏属性。如果我使用选择字符串,我会丢失文件名。 *.bat 作为 -filter 传递速度。

          get-childitem -recurse . *.bat | get-content -tail 1 | where { $_ -match 'rem' } | 
            select pspath
          
          
          PSPath
          ------
          Microsoft.PowerShell.Core\FileSystem::C:\users\js\foo\file.bat
          
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-07-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多