While döngüsünü kullanarak iki girdi dosyasından nasıl okunur


27

Bir kerede bir satır döngü yaparken iç içe iki giriş dosyasından herhangi bir okumanın bir yolu olup olmadığını bilmek istedim. Örneğin, iki dosya var diyelim FileAve FileB.

Dosya:

[jaypal:~/Temp] cat filea
this is File A line1
this is File A line2
this is File A line3

FileB:

[jaypal:~/Temp] cat fileb
this is File B line1
this is File B line2
this is File B line3

Geçerli Örnek Komut Dosyası:

[jaypal:~/Temp] cat read.sh 
#!/bin/bash
while read lineA
    do echo $lineA 
    while read lineB
        do echo $lineB 
        done < fileb
done < filea

Yürütme:

[jaypal:~/Temp] ./read.sh 
this is File A line1
this is File B line1
this is File B line2
this is File B line3
this is File A line2
this is File B line1
this is File B line2
this is File B line3
this is File A line3
this is File B line1
this is File B line2
this is File B line3

Sorun ve istenen çıktı:

Bu, FileA'daki her satır için tamamen FileB üzerinden döner. Devam et, ara ver, çıkmayı denedim ama hiçbiri aradığım çıktıyı elde etmek için tasarlanmadı. Komut dosyasının File A'dan yalnızca bir satır ve FileB'den bir satır okuyup döngüden çıkmasını ve ikinci File A satırından ve B Dosyasının ikinci satırından devam etmesini istiyorum.

[jaypal:~/Temp] cat read1.sh 
#!/bin/bash
count=1
while read lineA
    do echo $lineA 
        lineB=`sed -n "$count"p fileb`
        echo $lineB
        count=`expr $count + 1`
done < filea

[jaypal:~/Temp] ./read1.sh 
this is File A line1
this is File B line1
this is File A line2
this is File B line2
this is File A line3
this is File B line3

Bu süre loop ile elde etmek mümkün mü?


@Codaddict tarafından harika bir çözüm burada: stackoverflow.com/a/4011824/4095830 ->paste -d '\n' file1 file2
14:48

Yanıtlar:


32

İlk dosyada bazı karakterlerin asla oluşmayacağından eminseniz, yapıştırmayı kullanabilirsiniz.

Varsayılan sınırlayıcı sekmesini kullanarak yapıştırma örneği:

paste file1 file2 | while IFS="$(printf '\t')" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

Kullanılan yapıştırma örneği @:

paste -d@ file1 file2 | while IFS="@" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

Karakterin ilk dosyada oluşmayacağının garanti edilmesi yeterlidir. Bunun nedeni , son değişkeni doldururken readgörmezden geleceğidir IFS. Böylece @, ikinci dosyada meydana gelse bile bölünmeyecektir.

Muhtemelen daha temiz bir kod için bazı bash özelliklerini kullanan pasta örneği:

while IFS=$'\t' read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done < <(paste file1 file2)

Bash özellikleri kullanıldı: ansi c string ( $'\t') ve bir alt kabuk probleminde while döngüsünü önlemek için proses değiştirme ( <(...)) .

Her iki dosyada da hiçbir karakterin oluşmayacağından emin değilseniz, o zaman dosya tanımlayıcılarını kullanabilirsiniz .

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done 3<file1 4<file2

Çok fazla test edilmedi. Boş satırlarda mola verebilir.

0, 1 ve 2 numaralı dosya tanımlayıcıları zaten stdin, stdout ve stderr için zaten kullanılıyor. 3 ve üzeri dosya tanımlayıcıları (genellikle) ücretsizdir. Bash el kitabı 9'dan büyük dosya tanımlayıcıları kullanması konusunda uyarır, çünkü bunlar "dahili olarak kullanılırlar".

Açık dosya tanımlayıcılarının kabuk işlevlerine ve dış programlara miras kaldığını unutmayın. Açık bir dosya tanıtıcısını miras alan fonksiyonlar ve programlar, dosya tanımlayıcısını okuyabilir (ve yazabilir). Bir işlev veya harici bir program çağırmadan önce gerekli olmayan tüm dosya tanımlayıcılarını kapatmaya dikkat etmelisiniz.

İşte meta-işten ayrılmış (paralel olarak iki dosyadan satır satır okuma) asıl çalışma (yazdırma) ile aynı program.

work() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  work "$f1" "$f2"
done 3<file1 4<file2

Şimdi iş kodu üzerinde kontrolümüz yokmuş gibi davranıyoruz ve bu kod, hangi nedenle olursa olsun dosya tanımlayıcı 3'ten okumaya çalışıyor.

unknowncode() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
  read -r yoink <&3 && printf 'yoink: %s\n' "$yoink"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  unknowncode "$f1" "$f2"
done 3<file1 4<file2

İşte bir örnek çıktı. İlk dosyadaki ikinci satırın döngüden "çalındığını" unutmayın.

f1: file1 line1
f2: file2 line1
yoink: file1 line2
f1: file1 line3
f2: file2 line2

Harici kodu (veya bu konuda herhangi bir kodu) çağırmadan önce dosya tanımlayıcılarını nasıl kapatmanız gerektiği aşağıda açıklanmıştır.

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  # this will close fd3 and fd4 before executing anycode
  anycode "$f1" "$f2" 3<&- 4<&-
  # note that fd3 and fd4 are still open in the loop
done 3<file1 4<file2

17

İki dosyayı farklı dosya tanımlayıcılarında açın . Yerleşik girişi, readistediğiniz dosyanın bağlı olduğu tanımlayıcıya yönlendirin. Bash / ksh / zsh read -u 3yerine, yerine yazabilirsiniz read <&3.

while IFS= read -r lineA && IFS= read -r lineB <&3; do
  echo "$lineA"; echo "$lineB"
done <fileA 3<fileB

Bu pasaj, en kısa dosya işlendiğinde durur. Bkz. Döngü sırasında iki dosyayı bir IFS'ye okuma - Bu durumda sıfır fark elde etmenin bir yolu var mı? Her iki dosyanın sonuna kadar işlem yapmaya devam etmek istiyorsanız.

Ayrıca bkz. Ek bir dosya tanımlayıcısı ne zaman kullanırsınız? dosya tanımlayıcıları hakkında ek bilgi için ve IFS = okuyorsanız , neden sık sık kullanılır, `IFS = yerine; okurken ... bir açıklaması için IFS= read -r.


Dosya tanıtıcısındaki ek bağlantılar için teşekkürler @Gilles.
jaypal singh

@Gilles belki de sizi yanlış anladım, ancak döngü işlemini en uzun dosyayı tamamen yapamadım (bu da benim durumumda her zaman $ fileA'dır), bu yüzden ayrı bir soruya dönüştürdüm: Bu fark giriş ve çıkış arasında bir fark görmüyor mu? unix.stackexchange.com/questions/26780/… bulabildiğim en yakın şey, sadece bir fark satırı bulmaktan farklıydı.
ixtmixilix

3

Bir kabuk betiği istediğinizi biliyorum, ancak pastekomuta bir göz atmak isteyebilirsiniz .


Sağol lutzky. pasteçok havalı.
jaypal singh

2

Aşağıdaki komutu deneyin:

paste -d '\n' inp1.txt inp2.txt > outfile.txt

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.