unix - начало и конец файла


131

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

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


Что значит «за один раз»?
cnicutar

@cnicutar т.е. не идет файл head -10, просматривая данные, а затем отдельно иду файл tail -10 и просматривая данные
вершина

@toop Если вам нужен реальный рабочий пример, см. stackoverflow.com/a/44849814/99834
sorin

Ответы:


208

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

(head; tail) < file.txt

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

cat file.txt | (head; tail)

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


54
Строго говоря, это не дает вам хвоста исходного файла, но хвост потока после того, headкак потребляет первые 10 строк файла. (Сравните это с head < file.txt; tail < file.txtфайлом, в котором меньше 20 строк). Это очень незначительный момент, о котором нужно помнить. (Но все же +1.)
Чепнер

15
Ницца. Если вам нужен промежуток между головой и хвостом: (голова; эхо; хвост) <file.txt
Саймон Хиббс

3
Интересно, почему и как это работает. Задал его как новый вопрос: stackoverflow.com/questions/13718242
zellyn 05

9
@nametal На самом деле, вы можете и не получить столько. Хотя отображаютсяhead только первые 10 строк своего ввода, нет гарантии, что он не потреблял его больше, чтобы найти окончание 10-й строки, оставляя для отображения меньшую часть ввода . less
chepner

20
К сожалению, ответ работает только в некоторых случаях. seq 100 | (head; tail)дает мне только первые 10 чисел. Только при гораздо большем размере ввода (например seq 2000) хвост получает некоторый ввод.
modular

18

ed это standard text editor

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

2
Что делать, если в файле больше или меньше 200 строк? А вы не знаете количество строк ab initio?
Пол

@Paul Я изменил sedкed
к

14

Для чистого потока (например, вывода из команды) вы можете использовать «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;'

1
+1 за поддержку потока. Вы можете повторно использовать stderr:COMMAND | { tee >(head >&2) | tail; } |& other_commands
jfs

2
кстати, он ломается для файлов, размер которых превышает размер буфера (8K в моей системе). cat >/dev/nullисправляет:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
jfs

Я любил решение, но после игры на аа , а я не заметил , что в некоторых случаях хвост работает до головы ... там не гарантируется порядок между headи tailкомандами: \ ...
Jan

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

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


4

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

Помимо этого, вам нужно будет написать свою собственную программу / сценарий.


1
Хорошо, я всегда использовал catи / headили tailтрубку, приятно знать, что я могу использовать их индивидуально!
Пол

Как я могу передать эти первые 10 + последние 10 в другую команду?
топ

1
@Paul - с 'your_program' как wc -l он возвращает 10 вместо 20
вершина

3
или без необходимости порождать подоболочку: { head file; tail file; } | prog(требуется
пробел

1
Ничего себе ... голосование "против" за ответ, очень похожий на ответы других (но с указанием времени до них) спустя почти два года, от человека, который предпочел не публиковать, почему они проголосовали против. Ницца!
mah

4

На основании комментария Дж. Ф. Себастьяна :

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

3

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

такие инструменты, как 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

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

Ура, это работает с конвейерным выводом, а не только с файлами: a.out | awk -v ...
Camille Goudeseune

действительно :) но это нормальное поведение awk, большинство программ командной строки работают с stdin при вызове без аргументов.
Samus_

1
Очень близко к желаемому поведению, но кажется, что для <10 строк он добавляет дополнительные новые строки.
sorin

3

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

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, что очень полезно для длительных задач.
  • progressbar на stderr, гарантируя, что точки прогресса отделены от головы + хвоста (очень удобно, если вы хотите передать stdout)
  • позволяет избежать возможного неправильного порядка ведения журнала из-за буферизации (stdbuf)
  • избегайте дублирования вывода, когда общее количество строк меньше, чем голова + хвост.

2

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

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] }' ; }

1

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

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

И, потом вызвать его из оболочки командной строки: head_and_tail filename_foo.


1

Первые 10 строк файла file.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


5
Зачем использовать cat, если можно просто вызвать head -10 file.txt?
jstarek

Можете ли вы сделать количество строк переменной, чтобы вызов выглядел примерно так: head_ tail (foo, m, n) - возвращение первых m и последних n строк текста?
ricardo

@ricardo , что предполагает написание Баш скрипт , который принимает 3 арг и передает их tailи headили функции по псевдониму-джеями его.
Пол


1

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

но используя псевдоним 'hat' Head and Tails

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


hat large.sql

0

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

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


3
Это работает для файлов известной длины, но не для файлов, длина которых неизвестна.
Кевин

0

Чтобы обрабатывать каналы (потоки), а также файлы, добавьте это в свой файл .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

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

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

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

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

0

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

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


0
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"

ПРИМЕЧАНИЕ . Переменная aFile содержит полный путь к файлу .


0

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

Вот как я недавно обработал это для ряда очень больших файлов 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

Потребляет стандартный ввод, но прост и работает в 99% случаев использования

head_and_tail

#!/usr/bin/env bash
COUNT=${1:-10}
IT=$(cat /dev/stdin)
echo "$IT" | head -n$COUNT
echo "..."
echo "$IT" | tail -n$COUNT

пример

$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.