Удалить верхнюю строку текстового файла с помощью PowerShell - PullRequest
43 голосов
/ 15 января 2010

Я пытаюсь просто удалить первую строку из примерно 5000 текстовых файлов, прежде чем импортировать их.

Я все еще очень плохо знаком с PowerShell, поэтому не уверен, что искать или как подойти к этому. Моя текущая концепция с использованием псевдокода:

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

Тем не менее, я не могу понять, как сделать что-то вроде содержит.

Ответы [ 10 ]

34 голосов
/ 11 февраля 2012

Хотя я действительно восхищаюсь ответом @hoge как за очень лаконичную технику, так и за функцию-обертку для ее обобщения, и я призываю поддержать ее, я вынужден комментировать два других ответа, которые используют временные файлы (он грызет в мне нравятся ногти на доске!).

Предполагая, что файл не очень большой, вы можете заставить конвейер работать в отдельных разделах - тем самым устраняя необходимость в временном файле - с разумным использованием скобок:

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

... или в краткой форме:

(gc $file | select -Skip 1) | sc $file
34 голосов
/ 15 января 2010

Это не самый эффективный в мире, но это должно работать:

get-content $file |
    select -Skip 1 |
    set-content "$file-temp"
move "$file-temp" $file -Force
10 голосов
/ 16 января 2010

Используя переменную запись, вы можете сделать это без временного файла:

${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" }
}
8 голосов
/ 01 августа 2012

Мне просто нужно было выполнить ту же задачу, и gc | select ... | sc занял 4 ГБ ОЗУ на моей машине при чтении файла объемом 1,6 ГБ. Он не завершился в течение как минимум 20 минут после прочтения всего файла (как сообщалось в Read Bytes в Process Explorer ), после чего мне пришлось его уничтожить.

Моим решением было использование более .NET-подхода: StreamReader + StreamWriter. Посмотрите этот ответ, чтобы найти отличный ответ на этот вопрос: В Powershell, какой самый эффективный способ разбить большой текстовый файл по типу записи?

Ниже мое решение. Да, он использует временный файл, но в моем случае это не имело значения (это был чертовски огромный файл создания и вставки таблиц 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
5 голосов
/ 15 июля 2014

Вдохновленный ответом AASoft , я решил немного улучшить его:

  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

Первое изменение привело к уменьшению времени обработки моего файла размером 60 МБ с 5.3s до 4s. Остальные изменения более косметичны.

3 голосов
/ 14 февраля 2013

Я только что узнал с веб-сайта:

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

Или вы можете использовать псевдонимы, чтобы сделать его коротким, например:

gci *.txt | % { (gc $_) | ? { (1) -notcontains $_.ReadCount } | sc -path $_ }
2 голосов
/ 01 февраля 2013
$x = get-content $file
$x[1..$x.count] | set-content $file

Просто так много. Длинное скучное объяснение следует. Get-content возвращает массив. Мы можем «индексировать» переменные массива, как показано в this и других сообщениях сценаристов.

Например, если мы определим переменную массива следующим образом,

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

, поэтому $ array возвращает

first item
second item
third item

тогда мы можем "проиндексировать" этот массив, чтобы получить только его первый элемент

$array[0]

или только его второй

$array[1]

или диапазон значений индекса от 2-го до последнего.

$array[1..$array.count]
1 голос
/ 20 августа 2010

skip` не сработало, поэтому мой обходной путь -

$LinesCount = $(get-content $file).Count
get-content $file |
    select -Last $($LinesCount-1) | 
    set-content "$file-temp"
move "$file-temp" $file -Force
0 голосов
/ 15 мая 2019

Другой подход для удаления первой строки из файла, используя метод множественного назначения См. Ссылка

 $firstLine, $restOfDocument = Get-Content -Path $filename 
 $modifiedContent = $restOfDocument 
 $modifiedContent | Out-String | Set-Content $filename
0 голосов
/ 09 июля 2013

Для небольших файлов вы можете использовать это:

& C: \ windows \ system32 \ more +1 oldfile.csv> newfile.csv | из-нуль

... но это не очень эффективно при обработке моего файла примера размером 16 МБ. Кажется, он не завершает и не снимает блокировку на newfile.csv.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...