【问题标题】:How to delete specific rows from multiple csv files in powershell?如何从PowerShell中的多个csv文件中删除特定行?
【发布时间】:2015-07-05 22:27:10
【问题描述】:

您好,我正在寻找一种从多个 csv 文件中删除第二行和最后一行的有效方法?我在一个目录中有大约 5000 个文件。 下面的代码将删除第一行。如果我使用参数 -skip 2。它将跳过前 2 行,但我需要保留第一行并删除第二行和最后一行。我也不确定 get-content set-content 是否适合处理如此大量的文件。

foreach ($file in gci *.csv ){
(gc $file) | Select-Object -Skip 1 | set-content $file
 }

【问题讨论】:

    标签: powershell rows


    【解决方案1】:

    简单说一下性能。我使用@TheMadTechnician 方法获取内容并比较了 3 种不同的输出方法。我为每个测试使用了 100 个 1MB 的输入文件。结果如下:

    使用 Out-File 覆盖内容耗时 1 分 32 秒。

    dir *.txt | %{
        $content = gc $_.FullName
        $content | select -First 1 | Out-File $_.FullName -Force
        $content[2..($content.count -2)]|Out-File $_.FullName -Append
    }
    

    使用 Set-Content 覆盖内容耗时 37 秒。

    dir *.txt | %{
        $content = gc $_.FullName
        $output = @($content | select -First 1 )
        $output += $content[2..($content.count -2)]
        $output | Set-Content $_.FullName -Force
    }
    

    使用 StreamWriter 覆盖内容需要 31 秒。

    dir *.txt | %{     
        $content = gc $_.FullName
        $output = @($content | select -First 1 )
        $output += $content[2..($content.count -2)]
        $sw = New-Object System.IO.StreamWriter($_.FullName,$false)
        $output | %{$sw.WriteLine($_)}
        $sw.close()
    }
    

    您可能希望针对您的特定情况研究这些不同的方法,但我一直发现 Out-File 比 Set-Content 或 StreamWriter 慢得多。

    【讨论】:

    • 感谢您的帮助,斯蒂芬。最后的代码真的很快。看来你的PS真的不错。你能再帮忙解决一个问题吗?可以使用类似的代码将日期格式(它存储在这些文件的第一列中)从 m/d/yyyy 更改为 mm/dd/yyyy 还是我需要发布另一个问题?
    • 我会发布另一个问题,因为它是另一个问题。尝试查看 get-date、import-csv、export-csv 和自定义日期时间格式提供程序,看看你能不能先弄清楚。
    • 很高兴知道 Out-File 如此缓慢。我不知道,但将来一定会考虑到这一点!很好的答案
    • 你好 Stephen。我刚刚发布了我上面提到的那个问题。如果你冷审查我的代码并建议如何改进它会很棒。谢谢你。约翰stackoverflow.com/questions/28399452/…
    【解决方案2】:

    您已经很接近了,我只需通过您的代码中的 Where 子句通过管道跳过每个文件中的第 1 项和第 -1 项。像这样:

    (gci *.csv )|ForEach{
        $file = $_
        $contents = gc $file
        $contents | Where{$_ -ne $contents[1] -and $_ -ne $contents[-1]} | out-file $file.fullname -force
    }
    

    我做了 ForEach 内联以确保 GCI 完成并且在您尝试执行 Out-File 时没有保持任何打开

    编辑:我刚刚意识到我的代码有一个潜在的缺陷,如果你在第 2 行或最后一行有重复的行,这些行也会被删除。我写这篇文章是假设你有类似下面的东西想要清理:

    Col1,Col2,Col3,Col4
    ---- ---- ---- ----
    Data,data,data,data
    data,data,data,data
    Log Created: 02/04/2015
    

    您想删除 --- 行和日志在末尾创建的注释。

    Edit2: 更好的解决方案可能是获取文件的内容,输出第一行,然后输出第 3 行到最后(减去 1 行)并将其附加到同一行文件。比如:

    (gci *.csv )|ForEach{
        $Path = $_.FullName
        $content = gc $Path
        $content|select -first 1|Out-File $Path -force
        $content[2..($content.count-2)]|Out-File $Path -Append
    }
    

    【讨论】:

    • 类似的错误在这里。 “无法将参数绑定到参数“路径”,因为它为空”。 $contents = gc $文件
    • 对不起,我在更改 ForEach 方法后忘记重新分配 $file。我现在已经更新了它,它应该可以工作了。
    • 谢谢它有效。唯一的问题是它很慢。对于 5000 个文件,运行时间超过 7 分钟。在谷歌上我发现了一些关于 IO.StreamReader 来读取大量文件,但我不确定它是否可以在我的情况下使用。
    【解决方案3】:

    也许不是最好的方法,但您可以使用-Index 并计算您想要的行。

    foreach ($file in gci *.csv ){
        $data = gc $file
        $data | Select-Object -Index (,0 + (2..($data.Count - 2))) | set-content $file
    }
    

    对于数组,索引从 0 开始,所以我们取那个并跳过记录 1。之后我们只取剩下的减去最后一个。 -Index 采用整数数组,因此我们创建一个值为 0 的单元素数组,并将索引从 2 附加到文件末尾减去最后一个(这是 -2 的来源,因为 .Count 从 1 开始)。

    【讨论】:

    • 他想要第一行,而不是第二行或最后一行。他想消除记录 1 和 -1
    • 已更新。谢谢@TheMadTechnician
    • 我在这里遇到错误。 “无法将值“Select”转换为类型“System.int32”。“输入字符串的格式不正确”
    • 是的...我通过将我的测试代码与您的代码混合在该错误上犯了另一个错误。如果您想再看一下... 也许它的性能更好
    • 非常感谢您的代码比 TheMadTechnician 的代码快 45% 这在处理大量文件时确实节省了大量时间。
    猜你喜欢
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-07
    • 2023-01-09
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    相关资源
    最近更新 更多