Bir dosyanın ilk birkaç ve son birkaç satırını gösterme komutu


23

Çok satırlı bir dosyam var ve her satırın başında bir zaman damgası var.

[Thread-3] (21/09/12 06:17:38:672) logged message from code.....

Bu nedenle, sık sık bu günlük dosyasındaki 2 şeyi kontrol ediyorum.

  1. İlk birkaç satır, küresel şartlara sahip ve başlangıç ​​zamanı da verilmiştir.
  2. Son birkaç satır, başka bir bilgi ile çıkış durumuna sahip.

Dosyanın sadece ilk ve son birkaç satırını görüntülememe izin verecek hızlı ve tek bir komut var mı?


2
Küresel koşullar nedir ve sizin için head and tailişe yaramıyor mu?
papatya

Bu benim günlük dosyamın bir parçası. Ayrıntılı olmaya çalışıyordum. Bunu görmezden gelebilirsin.
mtk

Çözümün bana çok yakışmış. Daha fazla kolaylık istiyorsanız, bir kabuk işlevi yapın (bir takma ad bile yapabilir).
vonbrand

@ vonbrand Sorun bilmiyorum kiN
Bernhard

@Bernhard, sed(1)uzman değilim , ama daha sonra kullanmak için bir şeyleri saklamak için yollar var. Belki de oraya bakmak öder. OTOH, muhtemelen daha yakından tanıdığım için, sıklıkla kullanılıyorsa, yapmak için bir Perl (ya da her neyse) senaryosunu hazırlarım.
vonbrand

Yanıtlar:


12

Tek komutla yapmak için sedveya kullanabilirsiniz awk. Bununla birlikte, hızda kaybedersiniz, nedense sedve awkyine de tüm dosyayı taramanız gerekir. Hız açısından, bir işlev yapmak veya her zaman tail+ kombinasyonuna geçmek daha iyidir head. Bu, giriş bir boru ise çalışmamanın bir dezavantajına sahiptir, ancak kabuğunuzun desteklemesi durumunda işlem yerine koyma işlemini kullanabilirsiniz (aşağıdaki örneğe bakın).

first_last () {
    head -n 10 -- "$1"
    tail -n 10 -- "$1"
}

ve sadece olarak başlat

first_last "/path/to/file_to_process"

işlem değişikliğine devam etmek (bash, zsh, sadece kabuklar gibi ksh):

first_last <( command )

ps. grep"global şartlarınız" olup olmadığını kontrol etmek için bile ekleyebilirsiniz .


-n 10varsayılan, hayır mı?
l0b0

@ l0b0 evet, varsayılan. -n 10burada gerekli değildir.
acele

20

@rush, head + tail komutunu büyük dosyalar için daha verimli kullanmak konusunda haklıdır, ancak küçük dosyalar için (<20 satır), bazı satırlar iki kez çıkarılabilir.

{ head; tail;} < /path/to/file

eşit derecede verimli olurdu, ancak yukarıdaki sorun olmazdı.


Acele çözüm aksine, bu bir POSIX kabuğunda çalışmıyor.
Marco,

2
@Marco Huh? Burada sadece POSIX yapıları kullanılmaktadır. Neyin yanlış gittiğini görüyorsun?
Gilles 'SO- kötülükten vazgeç'

2
@ Gilles Boşlukları özledim: {head; tail;} < filezsh ile çalışıyor ancak sh ile başarısız oluyor. { head; tail;} < fileher zaman çalışır. Gürültü için özür dilerim.
Marco,

@Marco, bununla ilgili bir sorun olsaydı head, kabuk ile değil , onunla olurdu . POSIX, headnormal dosyalar için imleci dosyaya bu 10 satırın hemen üzerinde bırakmayı gerektirir . POSIX dışı headuygulamalar için (GNU kafasının çok eski versiyonları bu örnekte uyumlu değildi, ancak on yıllardan bahsediyoruz) veya dosyanın aranamadığı durumlarda (boru veya soket gibi, ancak başka bir çözüm aynı sorunu olurdu).
Stéphane Chazelas,

1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
Stéphane Chazelas

9

{ head; tail; }Solüsyon çünkü boruların (veya priz ya da başka herhangi olmayan aranabilir dosyalar) üzerinde çalışma olmaz heado bloklar tarafından okur olarak çok fazla veri tüketmeye olabilir ve potansiyel ötesinde dosya içindeki imleci bırakarak bir boruya geri aday olmayacağını ne tailkastedilmektedir seçmek.

Böylece, kabuğunki gibi bir anda bir karakter okuyan bir araç kullanabilirsiniz read(burada baş satır ve kuyruk satırlarını argüman olarak alan bir işlev kullanarak).

head_tail() {
  n=0
  while [ "$n" -lt "$1" ]; do
    IFS= read -r line || { printf %s "$line"; break; }
    printf '%s\n' "$line"
    n=$(($n + 1))
  done
  tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5

veya tailörneğin awk olarak uygulayın:

head_tail() {
  awk -v h="$1" -v t="${2-$1}" '
    {l[NR%t]=$0}
    NR<=h
    END{
      n=NR-t+1
      if(n <= h) n = h+1
      for (;n<=NR;n++) print l[n%t]
    }'
}

İle sed:

head_tail() {
  sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),\$!{N;b1" -e '}' -e 'N;D'
}

(bazı seduygulamaların desen uzaylarının boyutunda düşük bir sınırlamaya sahip olmasına rağmen , kuyruk çizgilerinin sayısının büyük değerleri için başarısız olur).


4

bashİşlem değişikliğini kullanarak aşağıdakileri yapabilirsiniz:

make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null

Satırların düzenli olmalarının garanti edilmediğini, ancak 8kB'den daha uzun dosyalar için büyük olasılıkla olacağını unutmayın. Bu 8kB kesme, okuma arabelleğinin tipik boyutudur ve | {head; tail;}küçük dosyalar için neden çalışmadığıyla ilgilidir .

cat >/dev/nullTutmak için gerekli olan headcanlı boru hattı. Aksi takdirde teeerken ayrılır ve çıktı alırken tail, giriş yerine, sonuna değil de bir yere ait olur.

Son olarak, neden bir başkasına gitmek >/dev/nullyerine ? Aşağıdaki durumda:tail|

make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2  # doesn't work

headstdout tailkonsoldan ziyade boruya besleniyor , bu bizim istediğimiz şey değil.


Baş veya kuyruk istedikleri çıktıyı yazmayı bitirdiğinde, stdin'leri kapatıp çıkarlar. SIGPIPE'nin geldiği yer orası. Normalde bu iyi bir şey, çıktının geri kalanını atıyorlar, bu yüzden borunun diğer tarafının onu üretmek için zaman geçirmeye devam etmesi için hiçbir sebep yok.
derobert,

Emri yerine getirme ihtimalini yükselten nedir? Muhtemelen büyük bir dosya için olacak, çünkü taildaha uzun çalışması gerekiyor, ancak kısa girdiler için yaklaşık yarısının başarısız olacağını bekliyorum (ve görüyorum).
Gilles 'SO- kötülükten vazgeç'

SIGPIPE'yi tee >(head) >(tail)aynı nedenlerle ( >(...)bu arada hem zsh hem de bash tarafından desteklenen bir ksh özelliği olduğu için) alacaksınız . Yapabilirsin ... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)ama yine de bazı hatalı borular hata mesajları göreceksin tee.
Stéphane Chazelas,

Sistemimde (bash 4.2.37, coreutils 8.13) günü tailSIGPIPE değil tarafından öldürülme biridir teeve tailbir boruya yazmıyor. Yani bir şey olmalı kill(), değil mi? Ve bu sadece |sözdizimi kullandığımda olur . stracediyor ki teearamıyor kill()... yani belki bash?
Jander,

1
@Jander, 8k'dan fazla beslenmeyi deneseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
Stéphane Chazelas

3

Kullanarak ed(ki bu dosyanın tamamını RAM'e okuyacak):

# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%s\n' 'H' '1,10p' '$-10,$p' 'q' | ed -s file

Daha kısa:ed -s file <<< $'11,$-10d\n,p\nq\n'
don_crissti

2

Stephane'in bir fonksiyondaki ilk çözümü, böylece argümanları kullanabilirsiniz (herhangi bir Bourne benzeri veya POSIX kabuğunda çalışır):

head_tail() {
    head "$@";
    tail "$@";
}

Şimdi bunu yapabilirsiniz:

head_tail -n 5 < /path/to/file

Elbette bu, yalnızca bir dosyaya baktığınızı ve Stephane'nin çözümünün (düzenli) yalnızca normal (aranabilir) dosyalar üzerinde çalıştığını (güvenilir) çalıştığını varsayar.


2

İle -u( --unbufferedGNU) seçeneğiyle sedkullanabileceğiniz sed -u 2qbir tamponsuz alternatif olarak head -n2:

$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100

(head -n2;tail -n2)Son satırlar tarafından tüketilen girdi bloğunun bir parçası olduğunda başarısız olur head:

$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2

Bu en iyi cevap olmalı! ÇALIŞIYOR bir cazibe gibi!
Ben Usman,

1

Bugün böyle bir şeye rastladım; burada bir akıntının önünden sadece son hatta ve birkaç satırda ihtiyacım vardı.

sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'

Bunu şu şekilde okudum: ilk satırın içeriğiyle birlikte tutma alanını başlat, tutma alanına 2-3 satır ekle, EOF'da tutma satırına son satırı ekle, tutma ve desen alanını değiştir ve deseni yazdır alanı.

Belki de sahip sedolduğumdan daha fazla -fazına sahip biri , bu soruda belirtilen akışın son birkaç satırını yazdırmak için bunun nasıl genelleştirileceğini bulabilir, ancak buna ihtiyacım olmadı ve $adrese göre matematik yapmanın kolay bir yolunu bulamadım. içinde sedya da belki zaman sadece son birkaç satır içinde olacak şekilde yer tutmaya yöneterek EOFulaşılır.


1

Eğer kurulu ise Perl'i deneyebilirsiniz:

perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'

Bu, çoğu dosya için çalışır, ancak işlemden önce tüm dosyayı belleğe okur. Perl dilimlerini bilmiyorsanız, köşeli parantez içindeki "0" "ilk satırı al" ve "-3 ...- 1" "son üç satırı al" anlamına gelir. İkisini de ihtiyaçlarınıza göre uyarlayabilirsiniz. Gerçekten büyük dosyaları işlemeniz gerekiyorsa ('büyük olan' RAM'inize ve belki de takas boyutlarına bağlı olabilir), şunları yapmak isteyebilirsiniz:

perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'

biraz daha yavaş olabilir, çünkü her tekrarda bir dilim yapar, ancak dosya boyutunda bağımsızdır.

Her iki komut da hem borularda hem de normal dosyalarda çalışmalıdır.

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.