it-swarm.com.ru

unix - голова и хвост файла

Скажем, у вас есть текстовый файл, какая команда для просмотра 10 верхних и 10 нижних строк файла одновременно?

то есть, если файл имеет длину 200 строк, то просмотрите строки 1-10 и 190-200 за один раз. 

109
toop

Вы можете просто:

(head; tail) < file.txt

И если вам нужно использовать каналы по какой-то причине, то вот так:

cat file.txt | (head; tail)

Примечание: напечатает дублированные строки, если количество строк в file.txt меньше, чем строки заголовка по умолчанию + строки хвоста по умолчанию.

176
Aleksandra Zalcman

ed это standard text editor

$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
17
kev

Для чистого потока (например, вывод команды) вы можете использовать 'tee' для разветвления потока и отправки одного потока в заголовок и одного в хвост. Для этого необходимо использовать функцию '> (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;'
10
RantingNerd

head -10 file.txt; tail -10 file.txt

Кроме этого, вам нужно написать свою собственную программу/скрипт.

3
mah
(sed -u 10q; echo ...; tail) < file.txt

Еще одна вариация темы (head;tail), но она позволяет избежать начальной проблемы заполнения буфера для небольших файлов.

2
guest

Потребовалось немало времени, чтобы найти решение, которое, кажется, единственное, которое охватило все варианты использования (пока):

command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
          '{
               if (NR <= offset) print;
               else {
                   a[NR] = $0;
                   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]}
           }'

Список возможностей:

  • прямой вывод для головы (очевидно, что для хвоста это невозможно)
  • не использовать внешние файлы
  • прогрессбар одна точка для каждой строки после MAX_LINES, очень полезно для длительных задач.
  • прогрессбар на stderr, гарантирующий, что точки прогресса отделены от головы + хвоста (очень удобно, если вы хотите передать стандартный вывод)
  • позволяет избежать возможного неправильного порядка ведения журнала из-за буферизации (stdbuf)
  • избегайте дублирования вывода, когда общее количество строк меньше, чем голова + хвост.
2
sorin

проблема в том, что потоковые программы заранее не знают длину файла (потому что его может и не быть, если это реальный поток).

такие инструменты, как tail, буферизуют последние n строк и ждут окончания потока, затем печатают.

если вы хотите сделать это одной командой (и заставить ее работать с любым смещением и не повторять строки, если они перекрываются), вам придется подражать такому поведению, о котором я упоминал.

попробуйте это awk:

awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
2
Samus_

Первые 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

1
Paul

Я написал простое приложение на Python для этого: https://Gist.github.com/garyvdm/9970522

Он обрабатывает каналы (потоки), а также файлы.

1
Gary van der Merwe

Ну, вы всегда можете связать их вместе. Вот так, head fiename_foo && tail filename_foo. Если этого недостаточно, вы можете написать себе функцию bash в своем файле .profile или в любом используемом вами имени пользователя:

head_and_tail() {
    head $1 && tail $1
}

И позже вызовите его из командной строки: head_and_tail filename_foo.

1
S.R.I

Основано на комментарии Я.Ф. Себастьяна :

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 $1*2}'; } 3>&1
Н * 2 
 2 
 4 
 6 
1
modular

Чтобы обработать каналы (потоки), а также файлы, добавьте это в файл .bashrc или .profile:

headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; 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.

0
Camille Goudeseune

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

Вот как я недавно обработал это для ряда очень больших CSV-файлов, которые я анализировал:

$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done

Это напечатает первые 10 строк и последние 10 строк каждого файла, а также распечатает имя файла и некоторые многоточия до и после.

Для одного большого файла вы можете просто выполнить следующее для того же эффекта:

$ head somefile.csv && echo ... && tail somefile.csv
0
Jitsusama
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"

NOTE: переменная aFile содержит полный путь файла.

0
mark_infinite

Основываясь на том, что @Samus_ объяснил здесь о том, как работает команда @Aleksandra Zalcman, этот вариант удобен, когда вы не можете быстро определить, где начинается хвост, не считая линий.

{ head; echo "####################\n...\n####################"; tail; } < file.txt

Или, если вы начинаете работать с чем-то, кроме 20 строк, подсчет строк может даже помочь.

{ head -n 18; tail -n 14; } < file.txt | cat -n
0
Script Wolf

Чтобы напечатать первые 10 и последние 10 строк файла, вы можете попробовать это:

cat <(head -n10 file.txt) <(tail -n10 file.txt) | less

0
mariana.ft

опираясь на идеи выше (проверено bash & zsh) 

но с использованием псевдонима "шляпа" голова и хвост

alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '


hat large.sql
0
zzapper

Я долго искал это решение. Пробовал сам с помощью sed, но проблема с незнанием длины файла/потока была непреодолимой. Из всех вариантов, доступных выше, мне нравится решение awk Камиля Гудесена. Он отметил, что его решение оставило лишние пустые строки в выводе с достаточно маленьким набором данных. Здесь я приведу модификацию своего решения, которая удаляет лишние строки.

headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; 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] }' ; }
0
Michael Blahay

Почему бы не использовать sed для этой задачи?

sed -n -e 1,+9p -e 190,+9p textfile.txt

0
lik