代码之家  ›  专栏  ›  技术社区  ›  Buddy Lindsey

使用PowerShell删除文本文件的顶行

  •  41
  • Buddy Lindsey  · 技术社区  · 16 年前

    我试图在导入它们之前删除大约5000个文本文件的第一行。

    我对PowerShell还是很陌生,所以不确定要搜索什么或如何实现这一点。我当前使用伪代码的概念:

    set-content file (get-content unless line contains amount)
    

    9 回复  |  直到 9 年前
        1
  •  44
  •   Peter Mortensen Pieter Jan Bonestroo    9 年前

    这不是世界上最有效的,但这应该是可行的:

    get-content $file |
        select -Skip 1 |
        set-content "$file-temp"
    move "$file-temp" $file -Force
    
        2
  •  43
  •   Michael Sorens    14 年前

    虽然我真的很欣赏@hoge的答案,因为它有一个非常简洁的技术和一个概括它的包装函数,我鼓励大家投票支持它,但我不得不对另外两个使用临时文件的答案发表评论(它像黑板上的指甲一样咬我!)。

    假设文件不是很大,您可以通过明智地使用括号强制管道在离散段中运行,从而避免需要临时文件:

    (Get-Content $file | Select-Object -Skip 1) | Set-Content $file
    

    (gc $file | select -Skip 1) | sc $file
    
        3
  •  11
  •   Peter Mortensen Pieter Jan Bonestroo    9 年前

    使用变量表示法,无需临时文件即可完成:

    ${C:\file.txt} = ${C:\file.txt} | select -skip 1
    
    function Remove-Topline ( [string[]]$path, [int]$skip=1 ) {
      if ( -not (Test-Path $path -PathType Leaf) ) {
        throw "invalid filename"
      }
    
      ls $path |
        % { iex "`${$($_.fullname)} = `${$($_.fullname)} | select -skip $skip" }
    }
    
        4
  •  9
  •   Peter Mortensen Pieter Jan Bonestroo    9 年前

    gc | select ... | sc 在我的机器上读取1.6GB文件时占用了4GB内存。在中读取整个文件后,它至少有20分钟没有完成(如中读取字节所报告的) Process Explorer

    我的解决方案是使用更多的.NET方法: StreamReader + StreamWriter . In Powershell, what's the most efficient way to split a large text file by record type?

    下面是我的解决方案。是的,它使用了一个临时文件,但在我的例子中,这并不重要(这是一个异常庞大的SQL表创建和insert语句文件):

    PS> (measure-command{
        $i = 0
        $ins = New-Object System.IO.StreamReader "in/file/pa.th"
        $outs = New-Object System.IO.StreamWriter "out/file/pa.th"
        while( !$ins.EndOfStream ) {
            $line = $ins.ReadLine();
            if( $i -ne 0 ) {
                $outs.WriteLine($line);
            }
            $i = $i+1;
        }
        $outs.Close();
        $ins.Close();
    }).TotalSeconds
    

    它返回:

    188.1224443
    
        5
  •  6
  •   Oliver    8 年前

    灵感来自 AASoft's answer ,我去做了更多的改进:

    1. $i 比较 0
    2. 将执行过程包装到 try..finally
    3. 从文件的开头
    4. 使用变量 $p 引用当前目录的步骤

    这些更改导致以下代码:

    $p = (Get-Location).Path
    
    (Measure-Command {
        # Number of lines to skip
        $skip = 1
        $ins = New-Object System.IO.StreamReader ($p + "\test.log")
        $outs = New-Object System.IO.StreamWriter ($p + "\test-1.log")
        try {
            # Skip the first N lines, but allow for fewer than N, as well
            for( $s = 1; $s -le $skip -and !$ins.EndOfStream; $s++ ) {
                $ins.ReadLine()
            }
            while( !$ins.EndOfStream ) {
                $outs.WriteLine( $ins.ReadLine() )
            }
        }
        finally {
            $outs.Close()
            $ins.Close()
        }
    }).TotalSeconds
    

    5.3s 4s

        6
  •  3
  •   noam    13 年前
    $x = get-content $file
    $x[1..$x.count] | set-content $file
    

    就这么多。下面是冗长乏味的解释。获取内容返回一个数组。我们可以“索引”数组变量,如中所示 this other 编写脚本。

    例如,如果我们定义这样的数组变量,

    $array = @("first item","second item","third item")
    

    first item
    second item
    third item
    

    然后我们可以“索引到”该数组中,只检索它的第一个元素

    $array[0]
    

    或者只是它的第二个

    $array[1]
    

    或者 range 从第二个到最后一个的索引值。

    $array[1..$array.count]
    
        7
  •  3
  •   Peter Mortensen Pieter Jan Bonestroo    9 年前

    Get-ChildItem *.txt | ForEach-Object { (get-Content $_) | Where-Object {(1) -notcontains $_.ReadCount } | Set-Content -path $_ }
    

    也可以使用别名使其简短,如:

    gci *.txt | % { (gc $_) | ? { (1) -notcontains $_.ReadCount } | sc -path $_ }
    
        8
  •  1
  •   Emperor XLII    15 年前

    斯基普没有用,所以我的解决办法是

    $LinesCount = $(get-content $file).Count
    get-content $file |
        select -Last $($LinesCount-1) | 
        set-content "$file-temp"
    move "$file-temp" $file -Force
    
        9
  •  0
  •   Venkataraman R    7 年前

    使用多重赋值技术从文件中删除第一行的另一种方法。参考 Link

     $firstLine, $restOfDocument = Get-Content -Path $filename 
     $modifiedContent = $restOfDocument 
     $modifiedContent | Out-String | Set-Content $filename
    
        10
  •  -1
  •   danielmbarlow    13 年前

    对于较小的文件,您可以使用以下选项:

    &C:\windows\system32\more+1 oldfile.csv>newfile.csv |输出为空

    ... 但是它在处理我的16MB示例文件时不是很有效。它似乎没有终止并释放newfile.csv上的锁。

    推荐文章