unix - head DAN tail file


131

Katakanlah Anda memiliki file txt, apa perintah untuk melihat 10 baris teratas dan 10 baris teratas file secara bersamaan?

yaitu jika file tersebut panjangnya 200 baris, maka lihat baris 1-10 dan 190-200 dalam sekali jalan.


Apa maksudmu "dalam sekali jalan"?
cnicutar

@cnicutar yaitu. tidak akan kepala file -10 melihat data dan kemudian secara terpisah pergi file -10 dan melihat data
toop

@toop Jika Anda ingin contoh nyata, lihat stackoverflow.com/a/44849814/99834
sorin

Jawaban:


208

Anda cukup:

(head; tail) < file.txt

Dan jika Anda perlu menggunakan pipa untuk beberapa alasan maka seperti ini:

cat file.txt | (head; tail)

Catatan: akan mencetak baris duplikat jika jumlah baris dalam file.txt lebih kecil dari garis kepala standar + garis ekor standar.


54
Sebenarnya, ini tidak memberi Anda ekor file asli, tetapi ekor aliran setelah headmengkonsumsi 10 baris pertama file. (Bandingkan ini dengan head < file.txt; tail < file.txtpada file dengan kurang dari 20 baris). Hanya poin yang sangat kecil untuk diingat. (Tapi tetap +1.)
chepner

15
Bagus. Jika Anda ingin celah antara bagian kepala dan ekor: (kepala; gema; ekor) <file.txt
Simon Hibbs

3
Ingin tahu tentang mengapa / bagaimana ini bekerja. Ditanyakan sebagai pertanyaan baru: stackoverflow.com/questions/13718242
zellyn

9
@nametal Sebenarnya, Anda bahkan mungkin tidak mendapatkan sebanyak itu. Meskipun headhanya menampilkan 10 baris pertama dari inputnya, tidak ada jaminan bahwa itu tidak mengkonsumsi lebih banyak untuk menemukan baris ke-10 berakhir, menyisakan lebih sedikit input untuk lessditampilkan.
chepner

20
Maaf untuk mengatakan, tetapi jawabannya hanya berfungsi dalam beberapa kasus. seq 100 | (head; tail)memberi saya hanya 10 nomor pertama. Hanya pada ukuran input yang jauh lebih besar (seperti seq 2000) ekornya mendapat beberapa input.
modular

18

ed adalah standard text editor

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

2
Bagaimana jika file tersebut memiliki lebih dari 200 baris? Dan Anda tidak tahu jumlah garis ab initio?
Paul

@ Paul Saya sudah berubah sedmenjadied
kev

14

Untuk stream murni (misalnya output dari perintah), Anda dapat menggunakan 'tee' untuk memotong stream dan mengirim satu stream ke head dan satu ke tail. Ini memerlukan penggunaan fitur '> (daftar)' dari bash (+ / dev / fd / N):

( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )

atau menggunakan / dev / fd / N (atau / dev / stderr) ditambah subkulit dengan pengalihan rumit:

( ( 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

(Tidak satu pun dari ini akan bekerja dalam csh atau tcsh.)

Untuk sesuatu dengan kontrol yang sedikit lebih baik, Anda dapat menggunakan perintah perl ini:

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 untuk dukungan streaming. Anda dapat menggunakan kembali stderr:COMMAND | { tee >(head >&2) | tail; } |& other_commands
jfs

2
btw, rusak untuk file yang lebih besar dari ukuran buffer (8K pada sistem saya). cat >/dev/nullmemperbaikinya:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
jfs

Aku mencintai solusi, tapi setelah bermain untuk aa sementara saya melihat bahwa dalam beberapa kasus ekor berlari sebelum kepala ... ada ada dijamin pemesanan antara headdan tailperintah: \ ...
Jan

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

Hanya variasi lain pada (head;tail)tema, tetapi menghindari masalah isi buffer awal untuk file kecil.


4

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

Selain itu, Anda harus menulis program / skrip Anda sendiri.


1
Bagus, saya selalu menggunakan catdan headatau taildisalurkan, senang mengetahui bahwa saya dapat menggunakannya secara individual!
Paul

Bagaimana saya bisa mem-pipe 10 + 10 ini dulu ke perintah lain?
toop

1
@ Paul - dengan 'program_Anda sebagai wc -l mengembalikan 10 bukannya 20
toop

3
atau, tanpa harus menelurkan subkulit: { head file; tail file; } | prog(spasi di dalam kurung, dan tanda titik koma diperlukan)
glenn jackman

1
Wow ... suara untuk memiliki jawaban yang sangat mirip dengan yang lain (belum dicap sebelum) setelah hampir dua tahun, dari seseorang yang memilih untuk tidak memposting mengapa mereka memilih. Bagus!
mah

4

Berdasarkan komentar JF Sebastian :

cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1

Dengan cara ini Anda dapat memproses baris pertama dan lainnya secara berbeda dalam satu pipa, yang berguna untuk bekerja dengan data 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

masalahnya di sini adalah bahwa program yang berorientasi pada aliran tidak tahu panjang file di muka (karena mungkin tidak ada, jika itu adalah aliran nyata).

alat-alat seperti tailbuffer n baris terakhir terlihat dan menunggu akhir aliran, lalu cetak.

jika Anda ingin melakukan ini dalam satu perintah (dan membuatnya bekerja dengan offset apa pun, dan jangan ulangi baris jika tumpang tindih) Anda harus meniru perilaku yang saya sebutkan ini.

coba awk ini:

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

perlu lebih banyak pekerjaan untuk menghindari masalah ketika offset lebih besar dari file
Samus_

Yay, ini bekerja dengan output pipa, bukan hanya file: a.out | awk -v ...
Camille Goudeseune

memang :) tapi itu perilaku normal awk, sebagian besar program commandline bekerja pada stdin ketika dipanggil tanpa argumen.
Samus_

1
Sangat dekat dengan perilaku yang diinginkan tetapi tampaknya untuk <10 baris tidak menambah baris baru.
sorin

3

Butuh banyak waktu untuk menyelesaikan dengan solusi ini yang, tampaknya menjadi satu-satunya yang mencakup semua kasus penggunaan (sejauh ini):

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

Daftar fitur:

  • output langsung untuk kepala (jelas bahwa untuk ekor tidak mungkin)
  • tidak menggunakan file eksternal
  • progressbar satu titik untuk setiap baris setelah MAX_LINES, sangat berguna untuk tugas yang berjalan lama.
  • progressbar pada stderr, memastikan bahwa titik-titik kemajuan dipisahkan dari kepala + ekor (sangat berguna jika Anda ingin memasang pipa stdout)
  • Menghindari kemungkinan salah logging karena buffering (stdbuf)
  • hindari duplikasi output ketika jumlah total baris lebih kecil dari head + tail.

2

Saya telah mencari solusi ini untuk sementara waktu. Mencoba sendiri dengan sed, tetapi masalah dengan tidak mengetahui panjang file / stream sebelumnya tidak dapat diatasi. Dari semua opsi yang tersedia di atas, saya suka solusi awk Camille Goudeseune. Dia memang membuat catatan bahwa solusinya meninggalkan garis kosong tambahan dalam output dengan set data yang cukup kecil. Di sini saya memberikan modifikasi dari solusinya yang menghilangkan garis ekstra.

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

Nah, Anda selalu bisa rantai mereka bersama. Seperti itu head fiename_foo && tail filename_foo,. Jika itu tidak cukup, Anda bisa menulis sendiri fungsi bash di file .profile Anda atau file login apa pun yang Anda gunakan:

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

Dan, kemudian memanggil dari shell Anda cepat: head_and_tail filename_foo.


1

10 baris file.ext pertama, lalu 10 baris terakhir:

cat file.ext | head -10 && cat file.ext | tail -10

10 baris terakhir file, lalu 10 baris pertama:

cat file.ext | tail -10 && cat file.ext | head -10

Anda kemudian dapat mengirimkan output ke tempat lain juga:

(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program


5
Mengapa menggunakan cat saat Anda cukup memanggil head -10 file.txt?
jstarek

Bisakah Anda membuat jumlah baris variabel, jadi panggilannya adalah seperti: head_ tail (foo, m, n) - mengembalikan m snd terakhir terakhir n baris teks?
Ricardo

@ricardo yang akan melibatkan penulisan skrip bash yang membutuhkan 3 argumen dan meneruskannya ke taildan headatau fungsi dengan alias-ing.
Paul


1

menggambar ide di atas (bash & zsh yang diuji)

tetapi menggunakan alias 'topi' Kepala dan Ekor

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


hat large.sql

0

Mengapa tidak digunakan seduntuk tugas ini?

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


3
Ini berfungsi untuk file dengan panjang yang diketahui, tetapi bukan file yang panjangnya tidak diketahui.
Kevin

0

Untuk menangani pipa (stream) dan juga file, tambahkan ini ke file .bashrc atau .profile Anda:

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

Maka Anda tidak bisa hanya

headtail 10 < file.txt

tetapi juga

a.out | headtail 10

(Ini masih menambahkan baris kosong palsu ketika 10 melebihi panjang input, tidak seperti biasa a.out | (head; tail). Terima kasih, penjawab sebelumnya.)

Catatan:, headtail 10tidak headtail -10.


0

Membangun apa yang dijelaskan oleh @Samus_ di sini tentang cara kerja perintah @Alexandra Zalcman, variasi ini berguna ketika Anda tidak dapat dengan cepat melihat di mana ekor dimulai tanpa menghitung garis.

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

Atau jika Anda mulai bekerja dengan sesuatu selain 20 baris, jumlah baris bahkan mungkin membantu.

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

0

Untuk mencetak 10 baris pertama dan 10 baris terakhir dari sebuah file, Anda dapat mencoba ini:

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


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

CATATAN : Variabel aFile berisi path lengkap file .


0

Saya akan mengatakan bahwa tergantung pada ukuran file, membaca isinya secara aktif mungkin tidak diinginkan. Dalam keadaan itu, saya pikir beberapa skrip shell sederhana sudah cukup.

Inilah cara saya baru-baru ini menangani ini untuk sejumlah file CSV yang sangat besar yang saya analisis:

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

Ini mencetak 10 baris pertama dan 10 baris terakhir dari setiap file, sementara juga mencetak nama file dan beberapa elipsis sebelum dan sesudah.

Untuk satu file besar, Anda bisa menjalankan yang berikut untuk efek yang sama:

$ head somefile.csv && echo ... && tail somefile.csv

0

Mengkonsumsi stdin, tetapi sederhana dan berfungsi untuk 99% kasus penggunaan

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

contoh

$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.