unix - dosyanın başı VE kuyruğu


131

Bir txt dosyanız olduğunu varsayalım, aynı anda dosyanın ilk 10 satırını ve alt 10 satırını görüntüleme komutu nedir?

yani dosya 200 satır uzunluğundaysa, 1-10 ve 190-200 satırlarını tek seferde görüntüleyin.


"Tek seferde" ne demek?
cnicutar

@cnicutar yani. Kafa -10 dosyayı gitmiyor verilere bakarak ve daha sonra ayrı ayrı kuyruk -10 dosyayı gidiyor ve verilere bakarak
toop

@toop Gerçek bir çalışma örneği istiyorsanız bkz. stackoverflow.com/a/44849814/99834
sorin

Yanıtlar:


208

Basitçe şunları yapabilirsiniz:

(head; tail) < file.txt

Ve herhangi bir nedenle boru kullanmanız gerekiyorsa, o zaman şöyle:

cat file.txt | (head; tail)

Not: file.txt dosyasındaki satır sayısı varsayılan head satırlarından + varsayılan kuyruk satırlarından daha küçükse yinelenen satırları yazdıracaktır.


54
Açıkçası, bu size orijinal dosyanın kuyruğunu vermez, ancak dosyanın headilk 10 satırını tükettikten sonra akışın kuyruğu verir . (Bunu head < file.txt; tail < file.txt20'den az satır içeren bir dosyada karşılaştırın ). Akılda tutulması gereken çok küçük bir nokta. (Ama yine de + 1.)
chepner

15
Güzel. Baş ve kuyruk kısımları arasında bir boşluk istiyorsanız: (baş; yankı; kuyruk) <dosya.txt
Simon Hibbs

3
Bunun neden / nasıl çalıştığını merak ediyorum. Yeni bir soru olarak soruldu: stackoverflow.com/questions/13718242
zellyn

9
@nametal Aslında, o kadarını bile alamayabilirsiniz. Girişinin headyalnızca ilk 10 satırını görüntülerken , 10. satır sonunu bulmak için daha fazlasını tüketmediğiless ve görüntülenecek daha az giriş bırakmadığı garanti edilmez .
chepner

20
Üzgünüm ama cevap sadece bazı durumlarda işe yarıyor. seq 100 | (head; tail)bana sadece ilk 10 rakamı veriyor. Sadece çok daha büyük girdi boyutunda (gibi seq 2000) kuyruk bir miktar girdi alır.
modüler

18

ed ... standard text editor

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

2
Ya dosyada 200'den fazla veya az satır varsa? Ve başlangıçtaki satır sayısını bilmiyor musunuz?
Paul

Ben değiştim @ Paul sediçined
kev

14

Saf bir akış için (örneğin bir komuttan çıktı), akışı çatallamak ve bir akışı başa diğerini kuyruğa göndermek için 'tee' kullanabilirsiniz. Bu, bash (+ / dev / fd / N) '> (liste)' özelliğinin kullanılmasını gerektirir:

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

veya / dev / fd / N (veya / dev / stderr) artı karmaşık yeniden yönlendirmeli alt kabuklar kullanarak:

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

(Bunların hiçbiri csh veya tcsh'de çalışmaz.)

Biraz daha iyi kontrole sahip bir şey için bu perl komutunu kullanabilirsiniz:

COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'

1
Akış desteği için +1. COMMAND | { tee >(head >&2) | tail; } |& other_commands
Stderr'i

2
btw, arabellek boyutundan (sistemimde 8K) büyük dosyalar için kırılıyor. cat >/dev/nulldüzeltir:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
jfs

Ben çözüm sevdi ama aa için oynama sonra kuyruk kafa önce çalışıyordu bazı durumlarda ... hayır arasında sipariş orada garantilidir fark ederken headve tailkomutlar: \ ...
Jan

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

(head;tail)Temanın başka bir varyasyonu , ancak küçük dosyalar için başlangıçtaki arabellek doldurma sorunundan kaçınma.


4

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

Bunun dışında kendi programınızı / betiğinizi yazmanız gerekecek.


1
Güzel, her zaman kullandım catve / headveya tailkullandım, onları ayrı ayrı kullanabileceğimi bilmek güzel!
Paul

Daha sonra bu ilk 10 + son 10'u başka bir komuta nasıl aktarabilirim?
2011'de

1
@Paul - 'your_program' olarak wc -l ile 20 yerine 10 döndürür
toop

3
veya bir alt kabuk oluşturmak zorunda kalmadan: { head file; tail file; } | prog( küme ayraçları arasındaki boşluk ve sondaki noktalı virgül gereklidir)
glenn jackman

1
Vay be ... Neredeyse iki yıl sonra, neden olumsuz oy verdiklerini yayınlamamayı seçen birinden, diğerlerine oldukça benzer bir yanıt (ancak onlardan önce zaman damgası olan) için olumsuz bir oy. Güzel!
mah

4

Dayanarak JF Sebastian'ın comment :

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

Bu şekilde, ilk satırı ve geri kalanını tek bir boruda farklı şekilde işleyebilirsiniz; bu, CSV verileriyle çalışmak için kullanışlıdır:

{ 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

Buradaki sorun, akış yönelimli programların dosyanın uzunluğunu önceden bilmemesidir (çünkü gerçek bir akışsa bir tane olmayabilir).

tailGörülen son n satırı arabelleğe alma gibi araçlar ve akışın sonunu bekler, ardından yazdırır.

Bunu tek bir komutta yapmak istiyorsanız (ve herhangi bir ofset ile çalışmasını istiyorsanız ve üst üste binerse satırları tekrarlamayın) bahsettiğim bu davranışı taklit etmelisiniz.

bunu deneyin 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

ofset dosyadan daha büyük olduğunda sorunları önlemek için daha fazla çalışmaya ihtiyacı var
Samus_

Yaşasın, bu yalnızca dosyalarla değil, borulu a.out | awk -v ...
çıktıyla da

gerçekten :) ama bu awk'nin normal davranışıdır, çoğu komut satırı programı argümansız çağrıldığında stdin üzerinde çalışır.
Samus_

1
İstenilen davranışa çok yakın, ancak 10'dan az satır için ekstra yeni satırlar eklediği görülüyor.
sorin

3

Tüm kullanım durumlarını kapsayan tek çözüm gibi görünen bu çözüme ulaşmak çok zaman aldı (şimdiye kadar):

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

Özellik listesi:

  • kafa için canlı çıktı (tabii ki kuyruk için mümkün değil)
  • harici dosya kullanımı yok
  • MAX_LINES'ten sonraki her satır için bir nokta ilerleme çubuğu, uzun süren görevler için çok kullanışlıdır.
  • stderr üzerinde ilerleme çubuğu, ilerleme noktalarının baş + kuyruktan ayrıldığından emin olur (stdout'u yönlendirmek istiyorsanız çok kullanışlıdır)
  • arabelleğe alma (stdbuf) nedeniyle olası yanlış günlük sırasını önler
  • toplam satır sayısı başlık + kuyruk'tan daha küçük olduğunda çıktının kopyalanmasından kaçının.

2

Bir süredir bu çözümü arıyordum. Sed ile kendim denedim, ancak önceden dosya / akışın uzunluğunu bilmeme sorunu aşılamazdı. Yukarıdaki tüm seçenekler arasında Camille Goudeseune'un awk çözümünü beğendim. Çözümünün çıktıda yeterince küçük bir veri kümesiyle fazladan boş satırlar bıraktığını not etti. Burada, fazladan çizgileri ortadan kaldıran çözümünün bir değişikliğini sunuyorum.

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

Eh, onları her zaman birbirine zincirleyebilirsiniz. Öyle gibi head fiename_foo && tail filename_foo. Bu yeterli değilse, .profile dosyanıza veya kullandığınız herhangi bir giriş dosyasına kendinize bir bash işlevi yazabilirsiniz:

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

Ve daha sonra istemi senin kabuğundan çağırmak: head_and_tail filename_foo.


1

File.ext'in ilk 10 satırı, ardından son 10 satırı:

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

Dosyanın son 10 satırı, ardından ilk 10:

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

Daha sonra çıktıyı başka bir yere de aktarabilirsiniz:

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


5
Sadece head -10 file.txt çağırabiliyorsanız neden cat kullanasınız?
jstarek

Satır sayısını değişken yapabilir misiniz, böylece çağrı şuna benzer: head_ tail (foo, m, n) - metnin ilk m ve son n satırını döndürmek?
ricardo

3 bağımsız değişken alır ve iletir bir bash betiği yazma ilgili olacağını @ricardo tailve headtakma-ing bunu veya bir fonksiyonu.
Paul


1

yukarıdaki fikirlere göre çizim (test edilmiş bash ve zsh)

ancak "şapka" Head and Tails takma adı kullanarak

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


hat large.sql

0

Neden sedbu görev için kullanılmasın ?

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


3
Bu, uzunluğu bilinen dosyalar için işe yarar, ancak uzunluğu bilinmeyen dosyalar için geçerli değildir.
Kevin

0

Dosyaları olduğu kadar boruları (akışları) da işlemek için bunu .bashrc veya .profile dosyanıza ekleyin:

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

O zaman sadece yapamazsın

headtail 10 < file.txt

Ayrıca

a.out | headtail 10

(Bu a.out | (head; tail), eski düzeyin aksine, 10 giriş uzunluğunu aştığında hala sahte boş satırlar ekler . Teşekkürler, önceki yanıtlayanlar.)

Not: headtail 10hayır headtail -10.


0

@ Samus_'un burada @Aleksandra Zalcman'ın komutunun nasıl çalıştığı hakkında açıkladığı şeye dayanarak, bu varyasyon, satırları saymadan kuyruğun nerede başladığını hızlıca anlayamadığınızda kullanışlıdır.

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

Ya da 20 satır dışında bir şeyle çalışmaya başlarsanız, satır sayısı yardımcı olabilir.

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

0

Bir dosyanın ilk 10 ve son 10 satırını yazdırmak için şunu deneyebilirsiniz:

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


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

NOT : aFile değişkeni dosyanın tam yolunu içerir .


0

Dosyanın boyutuna bağlı olarak, içeriğini aktif olarak okumanın istenmeyebileceğini söyleyebilirim. Bu durumda, bazı basit kabuk komut dosyalarının yeterli olacağını düşünüyorum.

Son zamanlarda analiz ettiğim çok büyük CSV dosyaları için bunu şu şekilde ele aldım:

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

Bu, her dosyanın ilk 10 satırını ve son 10 satırını yazdırırken, aynı zamanda dosya adını ve öncesinde ve sonrasında bazı üç nokta yazdırır.

Tek bir büyük dosya için, aynı etki için aşağıdakileri çalıştırabilirsiniz:

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

0

Standart kullanır, ancak basittir ve kullanım durumlarının% 99'u için çalışır

baş ve kuyruk

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

misal

$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.