unix - голова І хвіст файлу


131

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

тобто, якщо файл довжиною 200 рядків, то переглядайте рядки 1-10 та 190-200 за один раз.


Що ви маєте на увазі «за один раз»?
cnicutar

@cnicutar тобто. не збирається голова -10 файл, дивлячись на дані, а потім окремо збирається хвіст -10 файл і дивиться на дані
toop

@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

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

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

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', щоб розщедрити потік і надіслати один потік в голову і один в хвіст. Для цього потрібно використовувати або '> (список)' функцію 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
btw, він розбивається на файли більше розміру буфера (8 Кб у моїй системі). 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 в іншу команду?
toop

1
@Paul - з 'your_program' як wc -l повертається 10 замість 20
toop

3
або, не { head file; tail file; } | progпотребуючи
нерестування передпластини

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

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
N * 2
2
4
6

3

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

такі інструменти, як tailбуфер останніх п ятих рядків, які бачили, і чекають кінця потоку, після чого друкують.

якщо ви хочете зробити це в одній команді (і вона буде працювати з будь-яким зміщенням, і не повторюйте рядки, якщо вони перетинаються), вам доведеться наслідувати цю поведінку, яку я згадав.

спробуйте цей див:

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 ...
Каміль Гусенене

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

2

Я деякий час шукав це рішення. Спробував це сам із sed, але проблема з тим, що заздалегідь не знати довжини файлу / потоку, була непереборною. З усіх варіантів, доступних вище, мені подобається ексклюзивне рішення Каміля Годесюна. Він зробив зауваження, що його рішення залишило зайві порожні рядки у висновку з досить невеликим набором даних. Тут я надаю модифікацію його рішення, яка видаляє зайві лінії.

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
Навіщо використовувати кішку, коли ви можете просто зателефонувати заголовок -10 file.txt?
jstarek

Чи можете ви зробити кількість рядків змінними, тому виклик має щось на зразок: head_ tail (foo, m, n) - повернення першого m snd останніх n рядків тексту?
Рікардо

@ricardo, що передбачає написання bash-скрипту, який займає 3 аргументи та передає їх у tailта headабо функцію шляхом його псевдоніму.
Пол


1

малюнок на ідеях вище (перевірений bash & zsh)

але використовуючи псевдонім «капелюх» Голова і Хвости

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

Споживає stdin, але простий і працює на 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.