Yalnızca kabuk komut dosyası kullanarak metin dosyasından belirli bir satırı alın


101

Bir metin dosyasından belirli bir satır almaya çalışıyorum.

Şimdiye kadar çevrimiçi olarak sadece sed gibi şeyler gördüm (sadece sh -not bash veya sed veya bunun gibi herhangi bir şeyi kullanabilirim). Bunu yalnızca temel bir kabuk komut dosyası kullanarak yapmam gerekiyor.

cat file | while read line
    do
       #do something
    done

Yukarıda gösterildiği gibi satırları nasıl yineleyeceğimi biliyorum, ama ya sadece belirli bir satırın içeriğini almam gerekirse


satır numarasını biliyor musun
Mehul Rathod

1
O zaman sayarsın.
Ignacio Vazquez-Abrams

evet, satır numarası 5 @MehulRathod
GangstaGraham

3
Neden catiyi ama seddeğil mi? Bu hiç mantıklı değil.
William Pursell

5
Çünkü kimse hayır diyemez cat. Ah ... şirin cat!

Yanıtlar:


206

sed:

sed '5!d' file

awk:

awk 'NR==5' file

Sh komutuna ne dersiniz, sed, awk kullanamıyorum. Bunu soruda daha açık hale getirmeliyim.
GangstaGraham

@GangstaGraham, satırlar arasında nasıl yineleme yapılacağını bildiğini söyledin, bir sayaç eklemeye ne dersin? sayaç hedef satır numaranıza ulaşırsa, satırı alın ve döngüyü kırın. yardımcı olur mu?
Kent

4
@KanagaveluSugumar sed'in bilgi sayfasını okudu. 5!d5 hariç tüm satırları silmek anlamına gelir. shell var mümkündür, çift tırnağa ihtiyacınız vardır.
Kent

13
Başka bir varyant eklemenizi öneririm: sed -n 5pBu, yeni başlayanlar için hatırlanması daha mantıklı görünüyor, çünkü -n"varsayılan olarak çıktı yok" panlamına gelir ve "yazdır" anlamına gelir ve silmeden potansiyel olarak kafa karıştırıcı bir söz yoktur (insanlar dosyalardan bahsettiğinde, satırları silme eğilimindedir) farklı bir şey demek).
Josip Rodin

1
@JosipRodin haklısın, -n '5p'bu sorun için de çalışıyor. Buradaki fark , değişikliği dosyaya geri yazmak için 5!dekleme -iyapabilmenizdir. ancak yine -n 5pde yapmak zorundasınız sed -n '5p' f > f2&& mv f2 f, bu soru için sizin fikrinize katılıyorum.
Kent

22

Eğer ve linekullanabiliyorsanız, gerekli satır numaranızı tutan bir değişken olduğunu varsayarsak , oldukça basittir:headtail

head -n $line file | tail -1

Değilse, bu işe yaramalıdır:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

Bu -eqkarşılaştırma tamsayılar içindir, dolayısıyla satır içeriği ( $line) değil, satır numarası ister . Bu, örneğin want=5döngüden önce tanımlanarak ve ardından -eqkarşılaştırmayı kullanarak düzeltilmelidir $want. [reddedilmiş bir düzenlemeden taşındı]
Josip Rodin

1
@JosipRodin Kabul ettiğim için yorumunuza dayanarak bağımsız bir düzenleme önerisinde bulundum. Umarım bu sefer reddedilmeyecektir.
Victor Zamanian

16

Kullanabilirsin sed -n 5p file.

Örneğin bir aralık da alabilirsiniz sed -n 5,10p file.


11

En iyi performans yöntemi

sed '5q;d' file

Çünkü sed5. satırdan sonra herhangi bir satırı okumayı bırakır.

Bay Roger Dueck'ten güncelleme deneyi

Wcanadian-insane (6.6MB) yükledim ve time komutunu kullanarak sed -n 1p / usr / share / dict / words ve sed '1q; d' / usr / share / dict / word'leri karşılaştırdım; ilki 0.043 saniye, ikincisi sadece 0.002 saniye sürdü, bu nedenle 'q' kullanmak kesinlikle bir performans artışı!


1
Bu da yaygın olarak yazılır:sed -n 5q
William Pursell

3
Bu çözümü beğendim çünkü sed5. satırdan sonra herhangi bir satırı okumayı bırakıyor.
Anthony Geoghegan

2
I wcanadian-deli (6.6MB) alınması, karşılaştırılması yüklü sed -n 1p /usr/share/dict/wordsve sed '1q;d' /usr/share/dict/wordskullanılarak timekomut; ilki 0.043 saniye, ikincisi sadece 0.002 saniye sürdü, bu nedenle 'q' kullanmak kesinlikle bir performans artışı!
Roger Dueck

5

Örneğin, bir dosyanın 10 ile 20 arasındaki satırlarını almak istiyorsanız, bu iki yöntemin her birini kullanabilirsiniz:

head -n 20 york.txt | tail -11

veya

sed -n '10,20p' york.txt 

p Yukarıdaki komut, yazdırma anlamına gelir.

İşte görecekleriniz: görüntü açıklamasını buraya girin


2

Bu tür şeyleri yapmanın standart yolu, harici araçlar kullanmaktır. Bir kabuk komut dosyası yazarken harici araçların kullanımına izin vermemek saçmadır. Ancak, harici araçları gerçekten kullanmak istemiyorsanız, 5. satırı şu şekilde yazdırabilirsiniz:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Bunun mantıksal satır 5'i yazdıracağına dikkat edin. Yani input-filesatır devamları içeriyorsa, bunlar tek bir satır olarak sayılacaktır. -rOkuma komutuna ekleyerek bu davranışı değiştirebilirsiniz . (Muhtemelen istenen davranış budur.)


1
$((++i))bir bashizm gibi görünüyor; Operatörün harici araçların kullanımı kısıtlanmışsa, bir düzlükten fazlasına erişebileceklerini varsaymam/bin/sh
Josip Rodin

@JosipRodin Hayır, bu bir POSIX özelliğidir (ancak ++artış desteği özellikle isteğe bağlı olarak işaretlenmiştir).
üçlü

@tripleee, / bin / sh gibi modern çizgi ile çalışmıyor, bu yüzden ona güvenmem.
Josip Rodin

Ancak $((i+=1))Dash gibi basit bir geçici çözüm de işe yarıyor.
üçlü

$(($i+1))düşündüğüm basit bir çözüm.
Josip Rodin

1

William Pursell'in cevabına paralel olarak , işte orijinal v7 Bourne kabuğunda (ve dolayısıyla Bash'in bulunmadığı yerlerde) bile çalışması gereken basit bir yapı.

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Ayrıca breakaradığımız hattı elde ettiğimizde döngü dışı optimizasyona da dikkat edin .


0

Özellikle cevapların hiçbirini beğenmedim.

İşte böyle yaptım.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

Perl ile kolay! Bir dosyadan 1, 3 ve 5 numaralı satırları almak istiyorsanız, / etc / passwd deyin:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'ancak smartmatch deneysel ve kullanımı önerilmiyor
Sorin

Diğer çözümlerden hiçbiri bu kadar özlü değildir veya bu kadar esnekliğe izin vermez. (Neden zaman kazandıran ve işleri kolaylaştıran her şey "akıllı insanlar" tarafından "cesareti kırılmış" gibi görünüyor, hepimizin tüm gün ekranlara
bakmamız

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
En azından soruyu soran kişiye daha açık hale getirmek için bunun neden çalıştığını biraz açıklayabilir misiniz?
ted

Böylece ilk grep, başlangıçta satır numarası ekleyen tüm satırları seçer. Ardından ikinci grep, başlangıçta satır numarasını eşleştirerek belirli bir satırı seçer. Ve son olarak, satır numarası yankıdaki satır başlangıcından kesilir.
Oder

Bu, ile karşılaştırıldığında hem karmaşık hem de verimsizdir sed -n 5p, elbette ki buna benzer bir şeye optimize edilebilirsed -n '5!d;p;q'
üçlü
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.