Как использовать sed, чтобы удалить последние n строк файла

148

Я хочу удалить несколько n строк из конца файла. Можно ли это сделать с помощью sed?

Например, чтобы удалить строки от 2 до 4, я могу использовать

$ sed '2,4d' file

Но я не знаю номеров строк. Я могу удалить последнюю строку, используя

$sed $d file

но я хочу знать способ удалить n строк с конца. Пожалуйста, дайте мне знать, как это сделать, используя sed или какой-либо другой метод.

mezda
источник
2
@arashkordi: это использует счетчик строк сверху, а не снизу.
Ams
Связанный вопрос о суперпользователях .
Тор
// Может быть, стоит перефразировать вопрос, чтобы сделать его более общим, чем просто sed ?
Натан Басанезе
1
sed $d fileвозвращает ошибку Вместо этого $ d должно быть в кавычках, например:sed '$d' file
Akronix

Ответы:

221

Я не знаю sed, но это можно сделать с помощью head:

head -n -2 myfile.txt
военно-картографическая служба
источник
19
+1 для простоты. Эквивалентная команда sed довольно неприятна: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5для последних 5 строк).
Марк Б,
62
Обратите внимание, что это работает с некоторыми версиями head, но не является стандартным. Действительно, стандарт для headштатов:The application shall ensure that the number option-argument is a positive decimal integer.
Уильям Перселл
21
Чтобы добавить ответ @ WilliamPursell ... В оболочке Mac OS по умолчанию это не работает.
JDS
7
Ни на BSD, но вопрос помечен Linux.
Ams
3
минус 1, потому что, помимо osx, это не работает для меня на Ubuntu14 -head: illegal line count -- -2
Майкл Даррант
30

Если жесткое кодирование n является опцией, вы можете использовать последовательные вызовы sed. Например, чтобы удалить три последние строки, трижды удалите последнюю строку:

sed '$d' file | sed '$d' | sed '$d'
рукав моря
источник
2
Это, плюс -i, чтобы отредактировать файл на месте, а не выгрузить его на стандартный вывод, сработало для меня.
WAF
27

Из седьмой строки :

# delete the last 10 lines of a file
sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2

Кажется, это то, что вы ищете.

qstebom
источник
@Thor, вы можете изменить количество строк, удаляемых из файла, изменив 10in'$d;N;2,10ba'
Алексей Магура
1
@AlexejMagura: я комментировал более раннюю версию ответа.
Тор
22

Забавное и простое sedи tacрешение:

n=4
tac file.txt | sed "1,$n{d}" | tac

НОТА

  • двойные кавычки "необходимы для оболочки, чтобы оценить $nпеременную в sedкоманде. В одинарных кавычках интерполяция выполняться не будет.
  • tacявляется catобратными, смman 1 tac
  • {}в sedтам , чтобы отделить $nи d(если нет, то оболочка попытаться интерполировать не существующей $ndпеременной)
Жиль Квено
источник
7
Fyi нет tacна OSX
Майкл Даррант
1
@MichaelDurrant port info coreutils|| brew info coreutils;
голоса
19

Используйте sed, но пусть оболочка выполняет математические расчеты, с целью использования dкоманды, задав диапазон (для удаления последних 23 строк):

sed -i "$(($(wc -l < file)-22)),\$d" file

Чтобы удалить последние 3 строки, изнутри:

$(wc -l < file)

Дает количество строк в файле: скажем, 2196

Мы хотим удалить последние 23 строки, поэтому для левой стороны или диапазона:

$((2196-22))

Дает: 2174 Таким образом, оригинальная интерпретация sed после shell:

sed -i '2174,$d' file

Благодаря -iредактированию на месте, файл теперь содержит 2173 строки!

Если вы хотите сохранить его в новый файл, код:

sed -i '2174,$d' file > outputfile
LZC
источник
15

Вы можете использовать голову для этого.

использование

$ head --lines=-N file > new_file

где N - количество строк, которые вы хотите удалить из файла.

Содержимое исходного файла минус последние N строк теперь в new_file

Ричард
источник
8

Просто для полноты я хотел бы добавить свое решение. Я закончил делать это со стандартом ed:

ed -s sometextfile <<< $'-2,$d\nwq'

Это удаляет последние 2 строки с помощью на месте монтажа (хотя он действительно использовать временный файл /tmp!!)

Мишель
источник
5

Чтобы обрезать действительно большие файлы на месте, у нас есть truncateкоманда. Он не знает о строках, но tail+ wcможет преобразовывать строки в байты:

file=bigone.log
lines=3
truncate -s -$(tail -$lines $file | wc -c) $file

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

truncate -s $(head -n -$lines $file | wc -c) $file

Удобная однострочная, если вы не сможете войти в систему, введя пароль вместо имени пользователя:

truncate -s $(head -n -5 /var/log/secure | wc -c) /var/log/secure
Камил Христос
источник
4

Это может работать для вас (GNU sed):

sed ':a;$!N;1,4ba;P;$d;D' file
Potong
источник
Ницца. Вы пропустили окончание апостроф ( ').
Тор
извините за столь поздний комментарий, но я стараюсь и (с учетом моего AIX / posix) не удалось распечатать все, кроме последней строки. Читая код, я не понимаю, как удаляются последние четыре строки, явный цикл находится на 4 первых строках, поэтому он, безусловно, цикл после a d, Dили Pс GNU sed, а не в версии posix. Кажется, что строки не печатаются после Dи сохраняются в рабочем буфере для нового цикла без перехода к концу строки действий.
NeronLeVelu
@NeronLeVelu программа считывает в окне из 4 строк в пространство образца, а затем добавляет следующую строку и печатает первую, пока не достигнет конца файла, где удаляет оставшиеся строки.
потонг
@potong да, я тестирую GNU sed, и он сохраняет буфер между строками, где posix выгружает его после Dсоздания обратного эффекта.
NeronLeVelu
4

Большинство из приведенных выше ответов требуют команд / расширений GNU:

    $ head -n -2 myfile.txt
    -2: Badly formed number

Для немного более портативного решения:

     perl -ne 'push(@fifo,$_);print shift(@fifo) if @fifo > 10;'

ИЛИ

     perl -ne 'push(@buf,$_);END{print @buf[0 ... $#buf-10]}'

ИЛИ

     awk '{buf[NR-1]=$0;}END{ for ( i=0; i < (NR-10); i++){ print buf[i];} }'

Где «10» - это «n».

Темное сердце
источник
2

С ответами здесь вы уже узнали, что Sed не лучший инструмент для этого приложения.

Однако я думаю, что есть способ сделать это с помощью sed; Идея состоит в том, чтобы добавить N строк для хранения до тех пор, пока вы не сможете читать, не нажимая EOF. Когда нажмете EOF, распечатайте содержимое области удержания и выйдите.

sed -e '$!{N;N;N;N;N;N;H;}' -e x

Команда sed выше пропустит последние 5 строк.

Ришабх Сагар
источник
2

Попробуйте следующую команду:

n = line number
tail -r file_name | sed '1,nd' | tail -r
Ришав Каушал
источник
Пожалуйста, объясните, как это работает. К вашему сведению - чтобы инициализировать переменные оболочки, у нас не должно быть пробела вокруг =.
Codeforester
@codeforester Первая строка была комментарием, я думаю. Он просто использует tail -rтам, где другие использовали, tacчтобы изменить порядок строк и сделать последние nстроки nпервыми.
Николас Мелай,
2

Это можно сделать за 3 шага:

а) Подсчитайте количество строк в файле, который вы хотите редактировать:

 n=`cat myfile |wc -l`

б) Вычтите из этого числа количество удаляемых строк:

 x=$((n-3))

c) Скажите sed удалить из этой строки номер ( $x) до конца:

 sed "$x,\$d" myfile
Рей Лефевр
источник
Плохая математика Если вам нужно удалить 3 строки, то вычтите 3, но добавьте 1. С помощью математики, если в файле 10 строк, 10 - 3 = 7, и вы используете sedдля удаления строки с 7 по 10, то есть 4 строки, а не 3.
mathguy
1

Вы можете получить общее количество строк wc -l <file>и использовать

head -n <total lines - lines to remove> <file>

SWW13
источник
1

Это удалит последние 3 строки из file:

for i in $(seq 1 3); do sed -i '$d' file; done;

user5780554
источник
1
Это слишком дорого для больших файлов - весь файл должен быть прочитан и переписан n раз! И столько же вилок для sed.
Codeforester
хотя это, возможно, и не самое эффективное решение, оно гораздо проще и понятнее
Дхарам
0

Я предпочитаю это решение;

head -$(gcalctool -s $(cat file | wc -l)-N) file

где N - количество удаляемых строк.

p014k
источник
0
sed -n ':pre
1,4 {N;b pre
    }
:cycle
$!{P;N;D;b cycle
  }' YourFile

версия posix

NeronLeVelu
источник
0

Чтобы удалить последние 4 строки:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   
mstafreshi
источник
немного тяжелый (особенно в ресурсах) по сравнению с головой. Хотя бы tac file | sed '1,4d' | tacпотому, что сортировка, а затем удаление префикса стоит много ресурсов.
NeronLeVelu
@NeronLeVelu вы правы, но нет tacкоманды для некоторых систем (например, FreeBSD?)
mstafreshi
0

Я придумал это, где n - это количество строк, которые вы хотите удалить:

count=`wc -l file`
lines=`expr "$count" - n`
head -n "$lines" file > temp.txt
mv temp.txt file
rm -f temp.txt

Это немного кольцевой, но я думаю, что легко следовать.

  1. Подсчитайте количество строк в основном файле
  2. Вычтите количество строк, которые вы хотите удалить из подсчета
  3. Распечатайте количество строк, которые вы хотите сохранить и сохранить во временном файле
  4. Замените основной файл временным файлом
  5. Удалить временный файл
coyotetechy
источник
0

Для удаления последних N строк файла вы можете использовать ту же концепцию

$ sed '2,4d' file

Вы можете использовать комбо с хвостовой командой, чтобы перевернуть файл: если N равно 5

$ tail -r file | sed '1,5d' file | tail -r > file

И этот путь работает также там, где head -n -5 fileкоманда не запускается (как на Mac!).

Алессандра Биларди
источник
-2

Это удалит последние 12 строк

sed -n -e :a -e '1,10!{P;N;D;};N;ba'
Р. Кумар
источник
1
Небольшое объяснение поможет, пожалуйста.
Ричард Уайзман