2010-01-15 6 views
38

Прежде чем импортировать их, я пытаюсь просто удалить первую строку из примерно 5000 текстовых файлов.Удалить верхнюю строку текстового файла с помощью PowerShell

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

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

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

ответ

30

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

get-content $file | 
    select -Skip 1 | 
    set-content "$file-temp" 
move "$file-temp" $file -Force 
+0

Когда я пытаюсь запустить это, кажется, что это ошибка на -skip. Может быть, это может быть другая версия? – percent20

+1

-Skip является новым для Select-Object в PowerShell 2.0. Кроме того, если файлы все ascii, вы можете использовать set-content -enc ascii. Если кодировки смешаны, тогда это становится более сложным, если вы не заботитесь о кодировке файла. –

+0

Я установил powershell 2.0 и теперь работает. – percent20

10

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

${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" } 
} 
1

skip` Ждут» t, так что мое решение -

$LinesCount = $(get-content $file).Count 
get-content $file | 
    select -Last $($LinesCount-1) | 
    set-content "$file-temp" 
move "$file-temp" $file -Force 
29

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

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

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

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

(gc $file | select -Skip 1) | sc $file 
7

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

Моим решением было использование большего подхода .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 
+0

IIRC это потому, что скобки вокруг gc | select означают, что он считывает весь файл в память, прежде чем прокладывать его.В противном случае открытый поток заставляет set-content терпеть неудачу. Для больших файлов я думаю, что ваш подход, вероятно, лучший – Alex

+0

Спасибо, @AASoft, за отличное решение! Я позволил себе немного улучшить его, отбросив операцию сравнения в каждом цикле, ускоряя процесс примерно на 25% - см. [Мой ответ] (http://stackoverflow.com/a/24746158/177710). – Oliver

1
$x = get-content $file 
$x[1..$x.count] | set-content $file 

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

Например, если мы определим переменный массив, как это,

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

так $ массива возвращает

first item 
second item 
third item 

тогда мы можем «индекс в» массив для извлечения только его 1-ого элемента

$array[0] 

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

$array[1] 

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

$array[1..$array.count] 
3

Я только что узнал с сайта:

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

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

gci *.txt | % { (gc $_) | ? { (1) -notcontains $_.ReadCount } | sc -path $_ } 
+0

Большое спасибо за это решение. Не могли бы вы указать указанный вами веб-сайт? – giordano

-1

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

& C: \ windows \ system32 \ more +1 oldfile.csv> newfile.csv | out-null

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

4

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

  1. Избегайте переменную цикла $i и при сравнении с 0 в каждом цикле
  2. Wrap исполнение в 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 MB файл от 5.3s до 4s. Остальные изменения более косметичны.

+0

Вы можете добавить '-and! $ Ins.EndOfStream' в цикл' for', чтобы покрыть случаи, когда файл имеет меньше строк, чем '$ skip'. – AASoft

+0

Спасибо за головы! В этом есть смысл :-) – Oliver

Смежные вопросы