Примечание. Приведенные ниже решения исключают одну строку из любого текстового файла по номеру строки. Как указывает marsze , к CSV файлам могут применяться дополнительные соображения, где необходимо соблюдать осторожность, чтобы не удалить строку заголовка, и строки могут занимать несколько строк, если они имеют значения со встроенными символами новой строки. ; в этом случае лучше использовать синтаксический анализатор CSV.
Если производительность не имеет первостепенного значения, вот удобный для памяти способ на основе конвейера сделать это:
Get-Content file.txt |
Where-Object ReadCount -ne 1005 |
Set-Content -Encoding Utf8 new-file.txt
Get-Content
добавляет (несколько непонятно названное) свойство .ReadCount
к каждой строке, которую оно выводит, которая содержит номер строки на основе 1
.
Обратите внимание, что кодировка символов входного файла не сохраняется Get-Content
, поэтому вы должны явно контролировать Set-Content
'выходную кодировку, как показано выше, используя UTF-8 в качестве примера.
Не считывая весь файл в память целиком, вы должны вывести в новый файл, хотя бы временно; Вы можете заменить исходный файл временным файлом вывода на
Move-Item -Force new-file.txt file.txt
A более быстрая, но интенсивная память альтернатива , основанная на прямом использовании .NET Framework, которая также позволяет обновлять файл на месте:
$file = 'file.txt'
$lines = [IO.File]::ReadAllLines("$PWD/$file")
Set-Content -Encoding UTF8 $file -Value $lines[0..1003 + 1005..($lines.Count-1)]
Обратите внимание на необходимость использования "$PWD/$file"
, т. Е. Для явного добавления предварительного пути к текущему каталогу к относительному пути, хранящемуся в $file
, поскольку представление платформы .NET о том, чем является текущий каталог, отличается от представления PowerShell.
- Хотя
$lines = Get-Content $file
будет функционально эквивалентно $lines = [IO.File]::ReadAllLines("$PWD/$file")
, он будет работать заметно хуже.
0..1003
создает массив индексов от 0
до 1003
; +
объединяет этот массив с индексами 1005
через оставшуюся часть входного массива; обратите внимание, что индексы массива основаны на 0
, а номера строк - 1
.
Также обратите внимание, как результирующий массив передается в Set-Content
в качестве прямого аргумента через -Value
, что быстрее, чем передача его по конвейеру (... | Set-Content ...
), где элемент будет выполнена обработка по элементам.
Наконец, удобный для памяти метод, который быстрее, чем метод на основе конвейера :
$file = 'file.txt'
$outFile = [IO.File]::CreateText("$PWD/new-file.txt")
$lineNo = 0
try {
foreach ($line in [IO.File]::ReadLines("$PWD/$file")) {
if (++$lineNo -eq 1005) { continue }
$outFile.WriteLine($line)
}
} finally {
$outFile.Dispose()
}
Как и в случае команды на основе конвейера, вам, возможно, придется заменить исходный файл новым файлом.