18 ответов:
можно просто:
(head; tail) < file.txtи если вам нужно использовать трубы по какой-то причине, то вот так:
cat file.txt | (head; tail)Примечание: будет печатать дублированные строки, если количество строк в файле.txt меньше, чем линии по умолчанию головы + линии по умолчанию хвоста.
для чистого потока (например, выход из команды), вы можете использовать "тройник", чтобы разветвить поток и отправить один поток в голову и один в хвост. Для этого необходимо использовать либо функцию '> (list) ' bash (+/dev/fd/N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )или с помощью /dev/fd /N (или/dev / stderr) плюс дочерние ячейки со сложным перенаправлением:
( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1 ( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1(ни один из них не будет работать в csh или tcsh.)
для чего-то с немного лучшим контролем, вы можете использовать этот perl команда:
COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
head -10 file.txt; tail -10 file.txtкроме этого, вам нужно будет написать свою собственную программу / скрипт.
проблема здесь в том, что потоковые программы заранее не знают длину файла (потому что его может и не быть, если это реальный поток).
инструменты
tailбуферизуйте последние n строк и дождитесь конца потока, а затем распечатайте.Если вы хотите сделать это в одной команде (и заставить его работать с любым смещением и не повторять строки, если они перекрываются), вам придется эмулировать это поведение, о котором я упоминал.
попробуйте это awk:
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = ; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
Ну, вы всегда можете связать их вместе. Вот так,
head fiename_foo && tail filename_foo. Если этого недостаточно, вы можете написать себе функцию Bash в вашем .файл профиля или любой файл входа, который вы используете:head_and_tail() { head && tail }и, впоследствии вызвать его из командной строки:
head_and_tail filename_foo.
первые 10 строк файла.ext, затем его последние 10 строк:
cat file.ext | head -10 && cat file.ext | tail -10последние 10 строк файла, затем первые 10:
cat file.ext | tail -10 && cat file.ext | head -10затем вы можете передать вывод в другом месте:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
Я написал простое приложение python, чтобы сделать это: https://gist.github.com/garyvdm/9970522
он обрабатывает трубы (потоки), а также файлы.
опираясь на идеи выше (проверено bash & zsh)
но используя псевдоним 'шляпа' голова и хвосты
alias hat='(head -5 && echo "^^^------vvv" && tail -5) < ' hat large.sql
на основе комментарий Дж. Ф. Себастьяна:
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1таким образом, вы можете обрабатывать первую строку и остальные по-разному в одном канале, что полезно для работы с данными CSV:
{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print *2}'; } 3>&1N*2 2 4 6
чтобы обрабатывать трубы (потоки), а также файлы, добавьте это в свой .bashrc или .файл профиля:
headtail() { awk -v offset="" '{ if (NR <= offset) print; else { a[NR] = ; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }затем вы можете не только
headtail 10 < file.txt, но и
a.out | headtail 10(это все еще добавляет ложные пустые строки, Когда 10 превышает длину ввода, в отличие от простого старого
a.out | (head; tail). Спасибо, предыдущие ответчики.)Примечание:
headtail 10, а неheadtail -10.
потребовалось много времени, чтобы закончить с этим решением, которое, похоже, является единственным, которое охватывало все варианты использования (до сих пор):
command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \ '{ if (NR <= offset) print; else { a[NR] = ; delete a[NR-offset]; printf "." > "/dev/stderr" } } END { print "" > "/dev/stderr"; for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++) { print a[i]} }'список:
- живой выход для головы (очевидно, что для хвоста это невозможно)
- нет использования внешних файлов
- progressbar одна точка для каждой строки после MAX_LINES, очень полезно для длительных задач.
- progressbar на stderr, гарантируя, что точки прогресса разделены от головы + хвост (очень удобно, если вы хотите трубу stdout)
- избегает возможного неправильного порядка регистрации из-за буферизации (stdbuf)
- избегайте дублирования вывода, когда общее количество строк меньше, чем head + tail.
(sed -u 10q; echo ...; tail) < file.txtпросто еще одна вариация на
(head;tail)тема, но избегая начальной проблемы заполнения буфера для небольших файлов.
Я искал это решение в течение некоторого времени. Пробовал сам с sed, но проблема с незнанием длины файла/потока заранее была непреодолимой. Из всех вариантов, доступных выше, мне нравится решение awk от Camille Goudeseune. Он отметил, что его решение оставило дополнительные пустые строки на выходе с достаточно небольшим набором данных. Здесь я предоставляю модификацию своего решения, которое удаляет лишние строки.
headtail() { awk -v offset="" '{ if (NR <= offset) print; else { a[NR] = ; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
основываясь на том, что @Samus_ объяснил здесь о том, как работает команда @Aleksandra Zalcman, этот вариант удобен, когда вы не можете быстро определить, где начинается хвост без подсчета строк.
{ head; echo "####################\n...\n####################"; tail; } < file.txtили если вы начинаете работать с чем-то другим, чем 20 строк, количество строк может даже помочь.
{ head -n 18; tail -n 14; } < file.txt | cat -n
чтобы напечатать первые 10 и последние 10 строк файла, вы могли бы попробовать это:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
Comments