Удалить последнюю строку из файла с помощью PowerShell

Я использую

gc FileWithEmptyLines.txt | where {$_ -ne ""} > FileWithNoEmptyLines.txt

чтобы удалить пустые строки, которые SSRS помещает в нижней части моих CSVs.

однако последняя строка, в которой есть данные, заканчивается CRLF (как показано в Notepad++) - и это не удаляется, поэтому технически все еще есть пустая строка в нижней части файла.

есть ли способ удалить этот CRLF из последней строки (и сохранить данные нетронутыми, конечно)?

5 ответов


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

$stream = [IO.File]::OpenWrite('foo.txt')
$stream.SetLength($stream.Length - 2)
$stream.Close()
$stream.Dispose()

это усечение файла на месте. Он работает без чтения всего файла в память (очень хорошо, если у вас есть очень большой файл). Он работает для ASCII, латинского - * и UTF-8. Это не будет работать таким образом для UTF-16 (в этом случае вам придется удалить четыре байта с конца).

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

$stream = [IO.File]::Open('foo.txt', [IO.FileMode]::Open)
$stream.Position = $stream.Length - 2
$bytes = 0..1 | %{ $stream.ReadByte() }
$compareBytes = 13,10 # CR,LF
if ("$bytes" -eq "$compareBytes") {
    $stream.SetLength($stream.Length - 2)
}
$stream.Close()
$stream.Dispose()

опять же, адаптируйтесь, если вы используете другую кодировку, например, для UTF-16 вам нужно сравнить с любым 0,10,0,13 или 10,0,13,0.

согласен, это не очень PowerShell-ey, но с тех пор, как мне пришлось обрабатывать дамп базы данных 700 MiB, я опасаюсь полностью читать потенциально большие файлы в память ;)


при чтении файла с помощью Get-Content он передает каждую строку вниз по трубе в виде строки. Когда Out-File (по сути то, что > является псевдонимом для) получает эти строки, он всегда добавляет последовательность Терминатора строки. Попробуйте следующее, если файлы не слишком большие:

$text = [IO.File]::ReadAllText("c:\FileWithEmptyLinesAtEnd.txt")
[IO.File]::WriteAllText("c:\FileWithEmptyLinesAtEnd.txt", $text.TrimEnd())

это файл перед:

14> fhex .\FileWithEmptyLinesAtEnd.txt

Address:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ASCII
-------- ----------------------------------------------- ----------------
00000000 73 65 72 76 65 72 31 2C 73 65 72 76 65 72 32 2E server1,server2.
00000010 64 6F 6D 61 69 6E 2E 6C 6F 63 61 6C 2C 73 65 72 domain.local,ser
00000020 76 65 72 33 0D 0A 20 20 20 20 20 20             ver3..

и после:

19> fhex .\FileWithEmptyLinesAtEnd.txt

Address:  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ASCII
-------- ----------------------------------------------- ----------------
00000000 73 65 72 76 65 72 31 2C 73 65 72 76 65 72 32 2E server1,server2.
00000010 64 6F 6D 61 69 6E 2E 6C 6F 63 61 6C 2C 73 65 72 domain.local,ser
00000020 76 65 72 33                                     ver3

Я не уверен, насколько это применимо к этой ситуации, но мой поиск Google для удаления последней строки текстового файла привел меня сюда и приведенные выше примеры решений не работал. Это команда, которую я смог использовать, чтобы заставить это работать:

$file = "file.txt"
Get-Content $file | Measure-Object -Line
$a = (Get-Content $file | Measure-Object)
(Get-Content $file) | ? {($a.count-1)-notcontains $_.ReadCount} | Set-Content $file

Если вы работаете с большим файлом, вы можете сначала передать его во временный файл.


попробуйте это, хотя он удалит все пустые строки из вашего файла

(Get-Content c:\FileWithEmptyLinesAtEnd.txt) | 
Where-Object {$_ -match '\S'} | 
Out-File c:\FileWithEmptyLinesAtEnd.txt

для формата файла "UCS-2 Little Endian" используйте следующее:

$stream = [IO.File]::Open($filename, [IO.FileMode]::Open)
$stream.Position = $stream.Length - 4
$bytes = 0..3 | %{ $stream.ReadByte() }
$compareBytes = 13,0,10,0 # CR,LF
echo "bytes: "$bytes
if ("$bytes" -eq "$compareBytes")
{
    $stream.SetLength($stream.Length - 4)
}
$stream.Close()
$stream.Dispose()