Bir dosyadan n. Satır almak için Bash aracı


604

Bunu yapmanın "kanonik" bir yolu var mı? Kullanıyorumhead -n | tail -1Hangi hile yapar , ama özellikle bir dosyadan bir çizgi (veya satır aralığı) ayıklayan bir Bash aracı olup olmadığını merak ediyorum.

"Kanonik" derken, ana işlevi bunu yapan bir programdır.


10
"Unix yolu" kendi işini iyi yapan araçları zincirlemektir. Sanırım zaten çok uygun bir yöntem buldunuz. Diğer yöntemler içerir awkve sedeminim birisi Perl bir astar ya da benzeri ile gelebilir;)
0xC0000022L

3
Çift komut, head | tailçözümün en düşük düzeyde olmasını önerir . Neredeyse daha uygun başka çözümler önerilmiştir.
Jonathan Leffler

Ortalama bir vaka için hangi çözümün en hızlı olduğu konusunda herhangi bir kıyaslama yaptınız mı?
Marcin

5
En Deneyler (aralığı için) kedi çizgi X büyük bir dosya üzerinde Y satır üzerinde Unix ve Linux . (cc @Marcin, iki + yıl sonra hala merak ediyorsanız)
Kevin

6
head | tailGirilecek var olmayan bir çizgi sorgulamak eğer çözüm, işi yapmaz: son çizgi yazdırır.
jarno

Yanıtlar:


801

headve ile boru tailbüyük bir dosya için yavaş olacaktır. Şöyle öneririm sed:

sed 'NUMq;d' file

NUMYazdırmak istediğiniz çizginin sayısı nerede ; böylece, örneğin, sed '10q;d' file10. satır yazdırmak içinfile .

Açıklama:

NUMqsatır numarası olduğunda hemen kapanacaktır NUM.

dçizgiyi yazdırmak yerine siler; bu son satırda engellenir çünkü qbırakma sırasında komut dosyasının geri kalanının atlanmasına neden olur.

Eğer varsa NUMbir değişkende, bunun yerine tek bir çift tırnak kullanmak isteyeceksiniz:

sed "${NUM}q;d" file

44
Merak edenler için, bu çözüm 9-6 hakkında kat daha hızlı daha görünüyor sed -n 'NUMp've sed 'NUM!d'çözümleri aşağıda önerdi.
Skippy le Grand Gourou

75
Sanırım tail -n+NUM file | head -n1bu kadar hızlı ya da daha hızlı olacak. En azından, yarım milyon satırlı bir dosyada 250000 olan NUM ile denediğimde sistemimde (önemli ölçüde) daha hızlıydı. YMMV, ama neden olacağını anlamıyorum.
rici

2
@rici (önceki yorumun revizyonu) Linux'ta (Ubuntu 12.04, Fedora 20), kullanmak catgerçekten daha hızlıdır (neredeyse iki kat daha hızlı), ancak yalnızca dosya henüz önbelleğe alınmamışsa . Dosya önbelleğe alındıktan sonra,cat performans aynı kalırken dosya adı bağımsız değişkeninin doğrudan kullanımı daha hızlıdır (yaklaşık 1/3 daha hızlı) . İlginçtir ki, OS X 10.9.3'te bunların hiçbiri herhangi bir fark yaratmıyor gibi görünüyor: cat/ no cat, dosya önbelleğe alınmış veya değil. @anubhava: benim için bir zevk.
mklement0

2
@SkippyleGrandGourou: Bu optimizasyonun özel doğası göz önüne alındığında, sayı aralığınız bile genel bir ifade olarak anlamsızdır . Tek genel paket şu şekildedir : (a) bu optimizasyon tüm girdilere güvenli bir şekilde uygulanabilir, (b) toplam hat sayısına göre aranan hattın dizinine bağlı olarak , etkiler hiçbirinden dramatik olmayacaktır .
mklement0

17
sed 'NUMqilk NUMdosyaları çıkarır ve ;dson satırı hariç tümünü siler.
anubhava

304
sed -n '2p' < file.txt

2. satır basacak

sed -n '2011p' < file.txt

2011 yılı

sed -n '10,33p' < file.txt

satır 10 - satır 33

sed -n '1p;3p' < file.txt

1. ve 3. satır

ve bunun gibi...

Sed ile satır eklemek için şunu kontrol edebilirsiniz:

sed: belirli bir konuma bir satır ekle


6
@RafaelBarbosa <bu durumda gerekli değildir. Basitçe, bu benim yönlendirmeleri kullanma tercihimdir, çünkü sıklıkla sed -n '100p' < <(some_command)- yani evrensel sözdizimi gibi yönlendirmeleri kullandım :). Daha az etkili değildir, çünkü yönlendirme kendini çatallarken kabukla yapılır, bu yüzden ... bu sadece bir tercih ... (ve evet, bir karakter daha uzun) :)
jm666

1
@ jm666 Aslında 2 karakter daha uzun çünkü normalde '<' ve fazladan bir boşluk '' koyduğunuzdan sonra <:)
rasen58

2
@ rasen58 de bir karakter mi? :) / tamam, şaka yapıyorum - haklısın / :)
jm666

1
@duhaime, eğer birinin optimizasyon yapması gerekiyorsa. Ancak IMHO "ortak" sorunlar için sorun değil ve fark göze çarpmıyor. Ayrıca, head/ tailçözer gelmez sed -n '1p;3p'senaryo - aka birçok komşu olmayan satırları yazdırmak ...
jm666

1
@duhaime tabii ki - not doğru ve gereklidir. :)
jm666

93

Bu sayfada önerilen çözümleri karşılaştırabileceğim benzersiz bir durum var ve bu yanıtı, önerilen çözümlerin her biri için dahil edilen çalışma süreleriyle birleştirmesi olarak yazıyorum.

Kurmak

Satır başına bir anahtar / değer çifti ile 3.261 gigabayt ASCII metin veri dosyası var. Dosya toplamda 3.339.550.320 satır içeriyor ve go-to Vim dahil olmak üzere denediğim herhangi bir editörde açılan meydan okur. Ben sadece satır ~ 500.000.000 etrafında başlattığım keşfettiğim bazı değerleri araştırmak için bu dosyayı alt kümeye gerekir.

Dosyada çok fazla satır olduğundan:

  • Verilerle yararlı bir şey yapmak için satırların yalnızca bir alt kümesini ayıklamak gerekir.
  • Değer verdiğim değerlere ulaşan her satırı okumak uzun zaman alacaktır.
  • Çözüm, önem verdiğim satırları okur ve dosyanın geri kalanını okumaya devam ederse, neredeyse 3 milyar alakasız satırı okumak için zaman harcar ve gerektiğinden 6 kat daha uzun sürer.

En iyi durum senaryom, dosyadaki diğer satırları okumadan dosyadan yalnızca tek bir satır ayıklayan bir çözümdür, ancak bunu Bash'de nasıl başaracağımı düşünemiyorum.

Akıl sağlığım açısından, kendi sorunum için ihtiyaç duyacağım 500.000.000 satırın tamamını okumaya çalışmayacağım. Bunun yerine, 3.339.550.320'den 50.000.000 numaralı satırı ayıklamaya çalışacağım (bu, tam dosyayı okumak gerektiğinden 60 kat daha uzun sürecek).

timeHer komutu karşılaştırmak için yerleşik olanı kullanacağım.

Temel

İlk önce head tailçözümün nasıl olduğunu görelim :

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

50 milyon satır için taban çizgisi 00: 01: 15.321, eğer 500 milyon satır için düz gidersem muhtemelen ~ 12.5 dakika olurdu.

kesmek

Bundan şüpheliyim, ama denemeye değer:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

Bu, 00: 05: 12.156'yı çalıştırdı, bu da taban çizgisinden çok daha yavaş! Durdurmadan önce dosyanın tamamını mı yoksa sadece 50 milyon satırını mı okuduğundan emin değilim, ancak bu sorun için geçerli bir çözüm gibi görünmüyor.

AWK

Çözümü sadece çalıştırdım exitçünkü tam dosyanın çalışmasını beklemeyecektim:

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

Bu kod 00: 01: 16.583'te çalıştırıldı, bu sadece ~ 1 saniye daha yavaş, ancak hala taban çizgisinde bir gelişme değil. Bu durumda, çıkış komutu hariç tutulursa, tüm dosyayı okumak yaklaşık ~ 76 dakika sürerdi!

Perl

Mevcut Perl çözümünü de çalıştırdım:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

Bu kod, taban çizgisinden ~ 2 saniye daha hızlı olan 00: 01: 13.146'da yayınlandı. Tam 500.000.000 üzerinde çalıştırırsam, muhtemelen ~ 12 dakika sürer.

sed

Tahtadaki en iyi cevap, işte benim sonucum:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

Bu kod, taban çizgisinden 3 saniye ve Perl'den ~ 0.4 saniye daha hızlı olan 00: 01: 12.705'te çalıştırıldı. Tam 500.000.000 satırda çalıştırsaydım, muhtemelen ~ 12 dakika sürecekti.

mapfile

Ben bash 3.1 var ve bu nedenle mapfile çözüm test edemez.

Sonuç

Görünüşe göre, çoğunlukla head tailçözümü geliştirmek zor . sedÇözelti en iyi ihtimalle verimde ~% 3 ​​artış sağlar.

(formülle hesaplanan yüzdeler % = (runtime/baseline - 1) * 100)

Sıra 50.000.000

  1. 00: 01: 12.705 (-00: 00: 02.616 =% -3.47) sed
  2. 00: 01: 13.146 (-00: 00: 02.175 =% -2.89) perl
  3. 00: 01: 15.321 (+00: 00: 00.000 = +% 0.00) head|tail
  4. 00: 01: 16.583 (+00: 00: 01.262 = +% 1.68) awk
  5. 00: 05: 12.156 (+00: 03: 56.835 = +% 314.43) cut

Sıra 500.000.000

  1. 00: 12: 07.050 (-00: 00: 26.160) sed
  2. 00: 12: 11.460 (-00: 00: 21.750) perl
  3. 00: 12: 33.210 (+00: 00: 00.000) head|tail
  4. 00: 12: 45.830 (+00: 00: 12.620) awk
  5. 00: 52: 01.560 (+00: 40: 31.650) cut

Sıra 3.338.559.320

  1. 01: 20: 54.599 (-00: 03: 05.327) sed
  2. 01: 21: 24.045 (-00: 02: 25.227) perl
  3. 01: 23: 49.273 (+00: 00: 00.000) head|tail
  4. 01: 25: 13.548 (+00: 02: 35.735) awk
  5. 05: 47: 23.026 (+04: 24: 26.246) cut

4
Tüm dosyanın / dev / null içine ne kadar süre katlandığını merak ediyorum. (Ya bu sadece bir sabit disk kıyaslaması
olsaydı

3+ gig metin dosyası sözlüğünün sahibi olmanız için sapkın bir dürtü hissediyorum. Gerekçe ne olursa olsun, bu
metinliği

51

İle awkoldukça hızlı:

awk 'NR == num_line' file

Bu doğru ise, varsayılan davranış awkolarak etkilemektedir: {print $0}.


Alternatif versiyonlar

Dosyanız çok büyük olursa exit, gerekli satırı okuduktan sonra daha iyi olur . Bu şekilde CPU zamanından tasarruf edersiniz Cevabın sonunda zaman karşılaştırmasına bakın .

awk 'NR == num_line {print; exit}' file

Bir bash değişkeninden satır numarasını vermek isterseniz şunları kullanabilirsiniz:

awk 'NR == n' n=$num file
awk -v n=$num 'NR == n' file   # equivalent

exitÖzellikle satırın dosyanın ilk bölümünde olması durumunda, ne kadar zaman tasarrufu sağlandığını görün:

# Let's create a 10M lines file
for ((i=0; i<100000; i++)); do echo "bla bla"; done > 100Klines
for ((i=0; i<100; i++)); do cat 100Klines; done > 10Mlines

$ time awk 'NR == 1234567 {print}' 10Mlines
bla bla

real    0m1.303s
user    0m1.246s
sys 0m0.042s
$ time awk 'NR == 1234567 {print; exit}' 10Mlines
bla bla

real    0m0.198s
user    0m0.178s
sys 0m0.013s

Yani fark 0.198s vs 1.303s, yaklaşık 6x kat daha hızlı.


Awk alan ayırma girişiminde bulunduğundan bu yöntem her zaman daha yavaş olacaktır. Alan ayırma yükü azaltılabilirawk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
kvantour

Bu yöntemdeki awk'ın gerçek gücü, dosya1, n2, dosya2, n3 veya dosya3 ... n1 satırını birleştirmek istediğinizde ortaya çıkar awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3. GNU awk ile bu hızlandırılabilir awk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3.
kvantour

@kvantour, GNU awk'ın sonraki dosyası bu tür şeyler için harikadır. Nasıl olur FS=RSda alan bölünmesini önler?
fedorqui 'SO' zarar vermeyi kes '15

1
FS=RSalan bölünmesini engellemez, ancak yalnızca 0 $ olanları ayrıştırır ve yalnızca bir alan atar, çünkü RSin$0
kvantour

@kvantour Bazı testler yapıyorum FS=RSve zamanlamalar arasında fark görmedim. Genişleyebilmen için bu konuda bir soru sormam ne olacak? Teşekkürler!
fedorqui 'SO' zarar vermeyi durdur '20

29

Testlerime göre, performans ve okunabilirlik açısından tavsiyem:

tail -n+N | head -1

Nistediğiniz satır numarasıdır. Örneğin tail -n+7 input.txt | head -1, dosyanın 7. satırını yazdırır.

tail -n+Nsatırdan başlayarak her şeyi yazdırır Nve head -1bir satırdan sonra durmasını sağlar.


Alternatif head -N | tail -1belki de biraz daha okunabilir. Örneğin, bu 7. satırı basacaktır:

head -7 input.txt | tail -1

Performans söz konusu olduğunda, daha küçük boyutlar için çok fazla fark yoktur, ancak tail | headdosyalar büyük olduğunda (yukarıdan) daha iyi performans gösterecektir .

En çok oy sed 'NUMq;d'alanı bilmek ilginç, ancak kutunun dışında kafa / kuyruk çözeltisinden daha az insan tarafından anlaşılacağını ve kuyruktan / kafadan daha yavaş olduğunu iddia edeceğim.

Testlerimde, her iki kuyruk / kafa versiyonu sed 'NUMq;d'tutarlı bir şekilde daha iyi performans gösterdi . Bu, yayınlanan diğer ölçütlerle uyumludur. Kuyrukların / kafaların gerçekten kötü olduğu bir durum bulmak zor. Bunlar da şaşırtıcı değil, çünkü bunlar modern bir Unix sisteminde yoğun bir şekilde optimize edilmesini beklediğiniz işlemler.

Performans farkları hakkında fikir edinmek için, bunlar büyük bir dosya (9.3G) için aldığım sayıdır:

  • tail -n+N | head -1: 3.7 saniye
  • head -N | tail -1: 4.6 saniye
  • sed Nq;d: 18.8 saniye

Sonuçlar farklılık gösterebilir, ancak performans head | tailve tail | headgenel olarak daha küçük girdilerle karşılaştırılabilir ve sedönemli bir faktörle (yaklaşık 5x kadar) her zaman daha yavaştır.

Karşılaştırmamı yeniden oluşturmak için aşağıdakileri deneyebilirsiniz, ancak geçerli çalışma dizininde bir 9.3G dosyası oluşturacağı konusunda uyarılırsınız:

#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3

seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
    time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time sed $pos'q;d' $file
done
/bin/rm $file

İşte makinemdeki bir çalışmanın çıktısı (SSD ve 16G belleğe sahip ThinkPad X1 Carbon). Son çalıştırmada her şeyin diskten değil, önbellekten geleceğini varsayıyorum:

*** head -N | tail -1 ***
500000000

real    0m9,800s
user    0m7,328s
sys     0m4,081s
500000000

real    0m4,231s
user    0m5,415s
sys     0m2,789s
500000000

real    0m4,636s
user    0m5,935s
sys     0m2,684s
-------------------------

*** tail -n+N | head -1 ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000

real    0m6,452s
user    0m3,367s
sys     0m1,498s
500000000

real    0m3,890s
user    0m2,921s
sys     0m0,952s
500000000

real    0m3,763s
user    0m3,004s
sys     0m0,760s
-------------------------

*** sed Nq;d ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000

real    0m23,675s
user    0m21,557s
sys     0m1,523s
500000000

real    0m20,328s
user    0m18,971s
sys     0m1,308s
500000000

real    0m19,835s
user    0m18,830s
sys     0m1,004s

1
Performans, head | tailvs arasında farklı mıdır tail | head? Yoksa hangi satırın yazdırıldığına bağlı mı (dosya başlangıcına veya dosya sonuna)?
wisbucky

1
@wisbucky Sert rakamlarım yok, ama ilk önce kuyruk kullanmanın bir dezavantajı ve ardından "kafa -1" de toplam uzunluğu önceden bilmeniz gerekiyor. Eğer bilmiyorsanız, önce saymanız gerekir, bu performans açısından bir kayıp olacaktır. Diğer bir dezavantaj, kullanımının daha az sezgisel olmasıdır. Örneğin, 1'den 10'a kadar rakamınız varsa ve 3. satırı almak istiyorsanız, "tail -8 | head -1" kullanmanız gerekir. Bu "kafa -3 | kuyruk -1" den daha eğilimli bir hatadır.
Philipp Claßen

üzgünüm, açık olmak için bir örnek eklemeliydim. head -5 | tail -1vs tail -n+5 | head -1. Aslında, bir test karşılaştırması yapan ve tail | headdaha hızlı olduğu başka bir cevap buldum . stackoverflow.com/a/48189289
wisbucky

1
@wisbucky Bahsettiğiniz için teşekkür ederiz! Bazı testler yaptım ve gördüğümden çizginin konumundan bağımsız olarak her zaman biraz daha hızlı olduğunu kabul etmeliyim. Bunu göz önüne alarak, cevabımı değiştirdim ve birisinin tekrarlamak istemesi durumunda karşılaştırmayı da dahil ettim.
Philipp Claßen

27

Vay, tüm olasılıklar!

Bunu dene:

sed -n "${lineNum}p" $file

veya Awk sürümünüze bağlı olarak bunlardan biri:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

( Veya komutunu denemeniz gerekebilirnawkgawk ).

Sadece o çizgiyi yazdıran bir araç var mı? Standart araçlardan biri değil. Ancak, sedmuhtemelen en yakın ve kullanımı en basit olanıdır.



21

Bu soru varlık burada yapmanın Bash (≥4) yolu, Bash etiketlenmiş: kullanılmasını mapfileile -s(atla) ve -n(sayısı) seçeneği.

Bir dosyanın 42. satırını almanız gerekiyorsa file:

mapfile -s 41 -n 1 ary < file

Bu noktada, aryalanlarının satırlarını içeren file(son satır dahil), ilk 41 satırı ( -s 41) atladığımız ve bir satırı ( -n 1) okuduktan sonra durduğumuz bir dizi olacak . Bu gerçekten 42. sıra. Yazdırmak için:

printf '%s' "${ary[0]}"

Bir dizi satıra ihtiyacınız varsa, 42–666 (dahil) aralığını söyleyin ve matematiği kendiniz yapmak istemediğinizi ve bunları stdout'a yazdırdığınızı söyleyin:

mapfile -s $((42-1)) -n $((666-42+1)) ary < file
printf '%s' "${ary[@]}"

Bu satırları da işlemeniz gerekiyorsa, sondaki yeni satırı saklamak gerçekten uygun değildir. Bu durumda -t(kırpma) seçeneğini kullanın :

mapfile -t -s $((42-1)) -n $((666-42+1)) ary < file
# do stuff
printf '%s\n' "${ary[@]}"

Bunu sizin için bir işleve sahip olabilirsiniz:

print_file_range() {
    # $1-$2 is the range of file $3 to be printed to stdout
    local ary
    mapfile -s $(($1-1)) -n $(($2-$1+1)) ary < "$3"
    printf '%s' "${ary[@]}"
}

Harici komut yok, sadece Bash yerleşik!


11

Ayrıca sed print ve quit komutlarını da kullanabilirsiniz:

sed -n '10{p;q;}' file   # print line 10

6
-nEğer adam sayfasına bir bakışta tarafından bulurdum mutlaka olarak opsiyon, her satırını yazdırmak için varsayılan eylemi devre dışı bırakır.
üçlü

In GNU sed tüm sedcevapları aynı hızda hakkındadır. Bu nedenle ( GNU için sed ) bu en iyi sedyanıttır, çünkü büyük dosyalar ve küçük n. Satır değerleri için zaman kazanır .
agc

7

Bunun için Perl'i de kullanabilirsiniz:

perl -wnl -e '$.== NUM && print && exit;' some.file

6

Büyük dosyalar için en hızlı çözüm, iki mesafe olması şartıyla her zaman kuyruk | başıdır:

  • dosyanın başlangıcından başlangıç ​​satırına. Haydi arayalımS
  • son satırdan dosyanın sonuna kadar olan mesafe. OlunE

bilinir. Sonra bunu kullanabiliriz:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

howmany sadece gereken satır sayısıdır.

Https://unix.stackexchange.com/a/216614/79743 adresinde daha fazla ayrıntı


1
Lütfen Sve Eöğelerini (bayt, karakter veya satır) netleştirin .
agc

6

Yukarıdaki tüm cevaplar doğrudan soruyu cevaplar. Ancak burada daha az doğrudan bir çözüm ama düşünceyi kışkırtmak için potansiyel olarak daha önemli bir fikir.

Hat uzunlukları n'inci hat önce dosyanın, tüm bayt keyfi olduğu için ihtiyaç okunması . Büyük bir dosyanız varsa veya bu görevi birçok kez tekrarlamanız gerekiyorsa ve bu işlem zaman alıcıysa, verilerinizi ilk etapta farklı bir şekilde depolayıp depolamayacağınızı ciddiye almalısınız.

Asıl çözüm, örneğin dosyanın başlangıcında, satırların başladığı konumları gösteren bir indekse sahip olmaktır. Bir veritabanı biçimi kullanabilir veya sadece dosyanın başına bir tablo ekleyebilirsiniz. Alternatif olarak, büyük metin dosyanıza eşlik edecek ayrı bir dizin dosyası oluşturun.

örneğin, yeni satırlar için karakter konumlarının bir listesini oluşturabilirsiniz:

awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx

sonra ile okumak tail, hangi aslındaseek doğrudan dosyadaki uygun noktaya kadar !

örneğin 1000 hattı almak için:

tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1
  • Awk "karakter farkında" olduğundan, kuyruk çalışmadığından, bu 2 bayt / çok baytlı karakterlerle çalışmayabilir.
  • Bunu büyük bir dosyaya karşı test etmedim.
  • Ayrıca bu cevaba bakınız .
  • Alternatif olarak - dosyanızı daha küçük dosyalara bölün!

5

CaffeineConnoisseur'un çok yararlı kıyaslama cevabının bir takibi olarak ... 'mapfile' yönteminin diğerleriyle ne kadar hızlı karşılaştırıldığını merak ettim (test edilmediğinden), bu yüzden kendimi hızlı ve kirli bir hız karşılaştırması denedim. Ben bash 4 kullanışlı var. Ben millet övgü şarkı söylüyor gibi ben, üst yanıt üzerine yorumlardan birinde belirtilen "kuyruk | kafa" yöntemi (kafa | kuyruk yerine) bir test attı. Kullanılan test dosyasının boyutu kadar bir şeyim yok; kısa sürede bulabildiğim en iyi 14M safkan dosya (boşluklar ayrılmış, 12000 satırın hemen altında uzun satırlar) oldu.

Kısa versiyon: mapfile, kesim yönteminden daha hızlı görünür, ancak her şeyden daha yavaştır, bu yüzden ona bir dud diyorum. kuyruk | kafa, OTOH, en hızlı olabilir gibi görünüyor, ancak bu boyutta bir dosya ile fark sed ile karşılaştırıldığında o kadar önemli değil.

$ time head -11000 [filename] | tail -1
[output redacted]

real    0m0.117s

$ time cut -f11000 -d$'\n' [filename]
[output redacted]

real    0m1.081s

$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]

real    0m0.058s

$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]

real    0m0.085s

$ time sed "11000q;d" [filename]
[output redacted]

real    0m0.031s

$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]

real    0m0.309s

$ time tail -n+11000 [filename] | head -n1
[output redacted]

real    0m0.028s

Bu yardımcı olur umarım!


4

Başkalarının bahsettiklerini kullanarak, bunun bash kabuğumda hızlı ve züppe bir işlev olmasını istedim.

Bir dosya oluşturun: ~/.functions

İçeriği ekleyin:

getline() { line=$1 sed $line'q;d' $2 }

Ardından bunu şu adrese ekleyin ~/.bash_profile:

source ~/.functions

Şimdi yeni bir bash penceresi açtığınızda, fonksiyonu şu şekilde çağırabilirsiniz:

getline 441 myfile.txt


3

\ N (normalde yeni satır) ile ayrılmış birden çok satırınız varsa. 'Kes' seçeneğini de kullanabilirsiniz:

echo "$data" | cut -f2 -d$'\n'

Dosyadan 2. satırı alacaksınız. -f3size 3. satırı verir.


1
Birden çok satırı görüntülemek için de kullanılabilir: cat FILE | cut -f2,5 -d$'\n'DOSYA'nın 2. ve 5. satırlarını görüntüler. (Ama düzeni
koruyamaz

2

Satır numarası olarak bir değişkenle sed kullanarak n. Satırı yazdırmak için:

a=4
sed -e $a'q:d' file

Burada '-e' bayrağı yürütülecek komut için komut dosyası eklemek içindir.


2
İki nokta üst üste sözdizimi hatasıdır ve noktalı virgül olmalıdır.
tripleee

2

Zaten çok iyi cevaplar. Ben şahsen awk ile giderim. Kolaylık sağlamak için, bash kullanıyorsanız, aşağıdakileri ekleyin ~/.bash_profile. Ve bir dahaki girişinizde (veya bu güncellemeden sonra .bash_profile kaynağınız varsa), dosyalarınızı iletmek için yeni bir şık "nth" işleviniz olacaktır.

Bunu yürütün veya ~ / .bash_profile dosyasına (bash kullanıyorsanız) bash'ı yeniden açın (veya yürütün source ~/.bach_profile)

# print just the nth piped in line nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; }

Ardından, kullanmak için basitçe borulayın. Örneğin,:

$ yes line | cat -n | nth 5 5 line


1

Bir göz aldıktan sonra üst cevap ve kriter , ben küçücük bir yardımcı işlevi uyguladık:

function nth {
    if (( ${#} < 1 || ${#} > 2 )); then
        echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
        return 1
    fi
    if (( ${#} > 1 )); then
        sed "$1q;d" $2
    else
        sed "$1q;d"
    fi
}

Temel olarak iki modada kullanabilirsiniz:

nth 42 myfile.txt
do_stuff | nth 42

0

Yukarıdaki cevaplardan bazılarını kısa bir bash betiğine koydum ve adlı dosyaya (ya da tercih ettiğiniz başka bir adla) get.shbağlayabileceğiniz bir dosyaya koydum /usr/local/bin/get.

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

İle çalıştırılabilir olduğundan emin olun

$ chmod +x get

Üzerinde kullanılabilir hale getirmek için bağlantı verin PATHile

$ ln -s get.sh /usr/local/bin/get

Sorumlu tadını çıkarın!

P

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.