【发布时间】:2010-01-15 19:38:17
【问题描述】:
我试图在导入前删除大约 5000 个文本文件的第一行。
我对 PowerShell 还是很陌生,所以不知道要搜索什么或如何解决这个问题。我目前使用伪代码的概念:
set-content file (get-content unless line contains amount)
但是,我似乎无法弄清楚如何执行包含之类的操作。
【问题讨论】:
标签: powershell
我试图在导入前删除大约 5000 个文本文件的第一行。
我对 PowerShell 还是很陌生,所以不知道要搜索什么或如何解决这个问题。我目前使用伪代码的概念:
set-content file (get-content unless line contains amount)
但是,我似乎无法弄清楚如何执行包含之类的操作。
【问题讨论】:
标签: powershell
虽然我真的很佩服@hoge 的答案,因为它提供了一种非常简洁的技术和一个概括它的包装函数,并且我鼓励对它进行投票,但我不得不对使用临时文件的其他两个答案发表评论(它啃我喜欢黑板上的指甲!)。
假设文件不大,您可以强制管道在离散的部分中运行——从而避免对临时文件的需要——明智地使用括号:
(Get-Content $file | Select-Object -Skip 1) | Set-Content $file
...或简称:
(gc $file | select -Skip 1) | sc $file
【讨论】:
这不是世界上最高效的,但这应该可行:
get-content $file |
select -Skip 1 |
set-content "$file-temp"
move "$file-temp" $file -Force
【讨论】:
使用变量表示法,无需临时文件即可:
${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" }
}
【讨论】:
我只需要执行相同的任务,gc | select ... | sc 在读取 1.6 GB 文件时占用了我机器上的 4 GB RAM。它在读取整个文件后至少 20 分钟没有完成(正如 Process Explorer 中的 Read Bytes 所报告的那样),此时我不得不杀死它。
我的解决方案是使用更多的 .NET 方法:StreamReader + StreamWriter。
请参阅此答案以获得讨论性能的绝佳答案:In Powershell, what's the most efficient way to split a large text file by record type?
以下是我的解决方案。是的,它使用了一个临时文件,但就我而言,这并不重要(这是一个巨大的 SQL 表创建和插入语句文件):
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
【讨论】:
受AASoft's answer的启发,我进一步改进了它:
$i和比较在每个循环中与0
try..finally 块中以始终关闭正在使用的文件$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
第一个更改使我的 60 MB 文件的处理时间从 5.3s 减少到 4s。其余的更改更具装饰性。
【讨论】:
-and !$ins.EndOfStream 添加到for 循环条件以涵盖文件行数少于$skip 的情况。
$x = get-content $file
$x[1..$x.count] | set-content $file
就这么多了。冗长无聊的解释如下。获取内容返回一个数组。我们可以“索引到”数组变量,如 this 和 other Scripting Guys 帖子中所示。
例如,如果我们这样定义一个数组变量,
$array = @("first item","second item","third item")
所以 $array 返回
first item
second item
third item
然后我们可以“索引”该数组以仅检索其第一个元素
$array[0]
或者只有它的第二个
$array[1]
或从第二个到最后一个索引值的range。
$array[1..$array.count]
【讨论】:
我刚从一个网站了解到:
Get-ChildItem *.txt | ForEach-Object { (get-Content $_) | Where-Object {(1) -notcontains $_.ReadCount } | Set-Content -path $_ }
或者你可以使用别名来缩短它,比如:
gci *.txt | % { (gc $_) | ? { (1) -notcontains $_.ReadCount } | sc -path $_ }
【讨论】:
skip` 不起作用,所以我的解决方法是
$LinesCount = $(get-content $file).Count
get-content $file |
select -Last $($LinesCount-1) |
set-content "$file-temp"
move "$file-temp" $file -Force
【讨论】:
另一种从文件中删除第一行的方法,使用多重赋值技术。参考Link
$firstLine, $restOfDocument = Get-Content -Path $filename
$modifiedContent = $restOfDocument
$modifiedContent | Out-String | Set-Content $filename
【讨论】:
对于较小的文件,您可以使用:
& C:\windows\system32\more +1 oldfile.csv > newfile.csv |外空
...但它在处理我的 16MB 示例文件时不是很有效。它似乎没有终止并释放对 newfile.csv 的锁定。
【讨论】: