Как получить обратное поведение для `tail` и` head`?

55

Есть ли способ head/ tailдокумент и получить обратный вывод; потому что вы не знаете, сколько строк в документе?

Т.е. я просто хочу получить все, кроме первых двух строк, foo.txtчтобы добавить к другому документу.

chrisjlee
источник

Ответы:

57

Вы можете использовать это для удаления первых двух строк:

tail -n +3 foo.txt

и это, чтобы лишить последних двух строк:

head -n -2 foo.txt

(при условии, что файл заканчивается \nна последний)


Так же, как для стандартного использования tailи headэти операции не являются разрушительными. Используйте, >out.txtесли вы хотите перенаправить вывод в новый файл:

tail -n +3 foo.txt >out.txt

В случае, если он out.txtуже существует, он перезапишет этот файл. Используйте >>out.txtвместо, >out.txtесли вы хотите, чтобы вывод был добавлен out.txt.

Стефан Хименес
источник
3
re "head, когда файл заканчивается на \n" . Он работает для всех отрицательных целых чисел, кроме тех, -n -0которые вообще ничего не возвращают , как если -n 0бы (используя: head (GNU coreutils) 7.4) ... Однако, когда присутствует трейлинг \n, -n -0выводит как и следовало ожидать от -, т.е. он печатает весь файл ... Так что он работает для всех ненулевых отрицательных значений ... но -0завершается неудачно, когда нет трейлинга\n
Peter.O
@fred: действительно странно ... (то же самое с 8.12 здесь).
Стефан Гименес
Эта операция разрушительна? Как я хочу, чтобы скопировать инверсию первых двух строк документа в другую?
chrisjlee
@ Крис: Нет, они просто печатают результат на своем «стандартном выходе», который обычно подключается к терминалу. Я добавил некоторые детали о том, как перенаправить вывод в некоторые файлы.
Стефан Гименес
7
head -n -2не совместим с POSIX .
10
9

Если вы хотите все, кроме первых N-1 строк, звоните tailс количеством строк +N. (Число - это номер первой строки, которую вы хотите сохранить, начиная с 1, т.е. +1 означает начало сверху, +2 означает пропуск одной строки и т. Д.).

tail -n +3 foo.txt >>other-document

Нет простого, портативного способа пропустить последние N строк. GNU headпозволяет head -n +Nв качестве аналога tail -n +N. В противном случае, если у вас есть tac(например, GNU или Busybox), вы можете объединить его с tail:

tac | tail -n +3 | tac

В частности, вы можете использовать фильтр awk (не проверено):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

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

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

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

Жиль "ТАК - перестань быть злым"
источник
В некоторых системах (например, в современном Linux) вы можете обрезать (свернуть) файл на месте в начале, но обычно только на величину, кратную размеру блока FS (поэтому в данном случае это не очень полезно).
Стефан Шазелас
3

Со tailстраницы руководства ( tailто есть GNU ):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Таким образом, следующий должен приложить все , но первые 2 строки somefile.txtв anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt
Стивен Понедельник
источник
3

Для удаления первых n строк можно использовать GNU sed. Например, если n = 2

sed -n '1,2!p' input-file

Имеется в !виду «исключить этот интервал». Как вы можете себе представить, более сложный результат может быть получен, например,

sed -n '3,5p;7p'

это покажет строку 3,4,5,7. Больше силы приходит от использования регулярных выражений вместо адресов.

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

enzotib
источник
1
Почему не просто sed 1,2d? Проще обычно лучше. Кроме того, ничто в ваших примерах не относится к GNU Sed; все ваши команды используют стандартные функции Sed POSIX .
Wildcard
1

Вы можете использовать, diffчтобы сравнить вывод head/ tailс исходным файлом, а затем удалить то же самое, получая обратное.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
Sokoban
источник
1

Хотя tail -n +4вывод файла, начинающегося с 4-й строки (все, кроме первых 3 строк), является стандартным и переносимым, его headаналог ( head -n -3все, кроме последних 3 строк) - нет.

Портативно, вы бы сделали:

sed '$d' | sed '$d' | sed '$d'

Или же:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(имейте в виду, что в некоторых системах, где sedимеется шаблонное пространство ограниченного размера, оно не масштабируется до больших значений n).

Или же:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
Стефан Шазелас
источник
1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Если <infileэто обычный, lseek()-able файл, то да , конечно, не стесняйтесь. Выше приведена полностью поддерживаемая POSIXly конструкция.

mikeserv
источник
0

Мой подход похож на Жиля, но вместо этого я просто переворачиваю файл с помощью команды cat и pipe, используя команду head.

tac -r thefile.txt | head thisfile.txt (заменяет файлы)

Abe
источник
0

Надеюсь, я ясно понял вашу потребность.

У вас есть несколько способов выполнить ваш запрос:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Где / etc / passwd - ваш файл

Второе решение может быть полезным, если у вас большой файл:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"
Матье Коаву
источник
0

Решение для BSD (macOS):

Удалить первые 2 строки:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Удалить последние 2 строки:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... не очень элегантно, но выполняет свою работу!

спектр
источник