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.

toop
sumber
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.

Aleksandra Zalcman
sumber
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
kev
sumber
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;'
RantingNerd
sumber
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.

tamu
sumber
4

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

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

mah
sumber
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
modular
sumber
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
Samus_
sumber
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.
Sorin
sumber
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] }' ; }
Michael Blahay
sumber
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.

SRI
sumber
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

Paul
sumber
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

Saya menulis aplikasi python sederhana untuk melakukan ini: https://gist.github.com/garyvdm/9970522

Ini menangani pipa (stream) serta file.

Gary van der Merwe
sumber
2
Akan lebih baik memposting bagian kode yang relevan.
fedorqui 'SO berhenti merugikan'
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
zzapper
sumber
0

Mengapa tidak digunakan seduntuk tugas ini?

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

lik
sumber
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.

Camille Goudeseune
sumber
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
Script Wolf
sumber
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

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

CATATAN : Variabel aFile berisi path lengkap file .

mark_infinite
sumber
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
Jitsusama
sumber
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
Taman Brad
sumber