Противоположность хвоста: все строки, кроме последних n строк

36

Как я могу отбросить последние n строк файла с помощью фильтра командной строки unix?

Это было бы как бы наоборот tail: tailотбрасывает первые n строк, но пропускает остальные, но я хочу, чтобы команда передавала все, кроме последних n строк.

К сожалению, я не нашел ничего подобного - headтоже не помогает. РЕДАКТИРОВАТЬ : по крайней мере в Solaris это не принимает отрицательные аргументы.

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

Ханс-Петер Стёрр
источник
К сведению: при использовании заголовка: поместив '-' перед числом с параметром -n, он печатает все строки каждого файла, но не последние N строк, как показано ниже,
G Koe

Ответы:

38

Если у вас есть GNU head, вы можете использовать

head -n -5 file.txt

распечатать все, кроме последних 5 строк file.txt.

Если не head -nпринимает отрицательных аргументов, попробуйте

head -n $(( $(wc -l file.txt | awk '{print $1}') - 5 )) file.txt
user570500
источник
11
(и молитесь, чтобы оно было file.txtдлиной не менее шести строк ...)
CVn
1
К сожалению, эта не-GNU версия также не работает с потоками
Armand
1
@ MichaelKjörling По крайней мере на Ubuntu, это не проблема. Если в файлах меньше строк, чем указано head, возвращается пустой вывод без ошибок.
Alphaaa
Если я не ошибаюсь, глава -n 5 напечатает первые 5 строк, не все, кроме последних 5 ...
pypmannetjies
8
head file.txt               # first 10 lines
tail file.txt               # last 10 lines
head -n 20 file.txt         # first 20 lines
tail -n 20 file.txt         # last 20 lines
head -20 file.txt           # first 20 lines
tail -20 file.txt           # last 20 lines
head -n -5 file.txt         # all lines except the 5 last
tail -n +5 file.txt         # all lines except the 4 first, starts at line 5
Кжетил С.
источник
1
Что это добавляет, что не было ответа в принятом ответе? Также, как и в случае с другими вашими ответами, несколько строк объяснения вашего ответа значительно улучшат его.
music2myear
1
очень хорошее резюме
ruanhao
5

Вот простой способ удалить последнюю строку, которая работает на BSD и т. Д.

sed '$d' input.txt

Выражение гласит «в последней строке, удалите его». Другие строки будут напечатаны, так как это sedповедение по умолчанию.

Вы можете связать их вместе, чтобы удалить несколько строк

sed '$d' input.txt | sed '$d' | sed '$d'

Правда, это немного жестко, но делает только один просмотр файла.

Вы также можете взглянуть на это, чтобы получить дополнительные ответы: https://stackoverflow.com/questions/13380607/how-to-use-sed-to-remove-last-n-lines-of-a-file

Вот одна строка, адаптированная из одного из моих любимых там:

N=10
sed -n -e ':a' -e "1,$N!{P;N;D;};N;ba"

Мне было весело расшифровывать это, и я надеюсь, что и вы тоже (: Он выполняет буферизацию Nстрок при сканировании, но в остальном довольно эффективен.

JWD
источник
3

Мне любопытно, почему вы думаете, что headэто не вариант

~$ man head
...
-n, --lines=[-]K
        print the first K lines instead of the first 10; 
        with the leading `-', print all but the last K lines of each file

Это, кажется, соответствует вашей цели, используя, например:

head -n -20 yourfile.txt
Андерс Р. Быструп
источник
5
Обратите внимание, что это относится только к GNU head. BSD headне имеет этой опции, поэтому этот ответ не будет работать в Solaris или других Unix-системах без GNU coreutils. ОП специально пометил это с помощью Unix и Unix-Utils.
Slhck
2
@slhck Не говоря уже о том, что ОП упомянул, что это для Соляриса.
CVn
К сожалению, кто-то удалил мое упоминание о Солярисе. Но я все равно должен был упомянуть, что версия head не поддерживает это.
Ханс-Петер Стёрр
1
Извините все. Я не заметил Solaris, и я не знал о различных версиях головы.
Андерс Р. Биструп
1
@hstoerr Solaris теперь в ваших тегах :)
slhck
0

Еще один способ сделать это, если tail -nне принимать отрицательные аргументы

tac file.txt | tail -n +6 | tac

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

atw31337
источник
Благодарность! Это отличная идея, которую никто не придумал. К сожалению, это было бы совершенно неэффективно для варианта использования, который я имел в виду с этим вопросом: если это большой файл, он будет не только полностью прочитан один или несколько раз, как с другими решениями, но, вероятно, также будет записан для диск во временные файлы, если он не помещается в память.
Ганс-Петер Стёрр
@ Ханс-Питер Очень верно. Решил написать скрипт на python3 для него. Попробуйте это github.com/atw31337/donkey . Я рекомендую использовать параметры вывода. Они работают намного быстрее, чем с помощью перенаправлений.
atw31337
Красиво написано! Однако он дважды читает файл, что не является обязательным, если вы буферизуете последние n строк, и это является проблемой для больших файлов. Лично мне это больше не нужно, но в случае, если вам весело улучшать это, и другим это нужно ... В конце концов, было несколько лайков и закладок по этому вопросу.
Ганс-Петер Стёрр
@ Ханс-Питер. Размер буфера будет зависеть от количества удаляемых строк. Это может быть проблемой, если из файла необходимо удалить очень большое количество строк. Чтобы избежать проблем, связанных с памятью, я переписал скрипт для использования метода подсчета строк с большими значениями n и метода буферизации с более низкими значениями n; однако после тестирования очень большого файла оригинальный метод подсчета строк все еще работает быстрее. Кажется, что накладные расходы на управление буфером перевешивают количество строк ... или я просто что-то упустил.
atw31337
Хорошо, но для BSD-варианта Mac OS X по умолчанию нет команды tac. :( Этот вид побеждает
сценарий