Döngüler için Çok Değişkenli


12

İçindeki fordöngülerde birden çok değişken (yalnızca tamsayılar değil) belirtmenin bir yolu var mı bash? Çalışmam gereken keyfi metin içeren 2 dosyam olabilir.

İşlevsel olarak neye ihtiyacım var böyle bir şey:

for i in $(cat file1) and j in $(cat file2); do command $i $j; done

Herhangi bir fikir?

Yanıtlar:


11

İlk olarak, satırları kelime okuma yoluyla okuma ile ilgili kaçınılmaz sorunlar olduğu için for ile okuma.

Eşit uzunlukta dosyalar varsayarsak veya yalnızca iki dosyadan daha kısa olana kadar döngü yapmak istiyorsanız, basit bir çözüm mümkündür.

while read -r x && read -r y <&3; do
    ...
done <file1 3<file2

Daha genel bir çözümü bir araya getirmek zordur çünkü readyanlış ve başka nedenlerle geri döner. Bu örnek, rastgele sayıda akışı okuyabilir ve en kısa veya en uzun girdiden sonra geri dönebilir.

#!/usr/bin/env bash

# Open the given files and assign the resulting FDs to arrName.
# openFDs arrname file1 [file2 ...]
openFDs() {
    local x y i arr=$1

    [[ -v $arr ]] || return 1
    shift

    for x; do
        { exec {y}<"$x"; } 2>/dev/null || return 1
        printf -v "${arr}[i++]" %d "$y"
    done
}

# closeFDs FD1 [FD2 ...]
closeFDs() {
    local x
    for x; do
        exec {x}<&-
    done
}

# Read one line from each of the given FDs and assign the output to arrName.
# If the first argument is -l, returns false only when all FDs reach EOF.
# readN [ -l ] arrName FD1 [FD2 ...]
readN() {
    if [[ $1 == -l ]]; then
        local longest
        shift
    else
        local longest=
    fi

    local i x y status arr=$1
    [[ -v $arr ]] || return 1
    shift

    for x; do
        if IFS= read -ru "$x" "${arr}[i]" || { unset -v "${arr}[i]"; [[ ${longest+_} ]] && return 1; }; then
            status=0
        fi
        ((i++))
    done
    return ${status:-1}
}

# readLines file1 [file2 ...]
readLines() {
    local -a fds lines
    trap 'closeFDs "${fds[@]}"' RETURN
    openFDs fds "$@" || return 1

    while readN -l lines "${fds[@]}"; do
        printf '%-1s ' "${lines[@]}"
        echo
    done
}

{
    readLines /dev/fd/{3..6} || { echo 'error occured' >&2; exit 1; }
} <<<$'a\nb\nc\nd' 3<&0 <<<$'1\n2\n3\n4\n5' 4<&0 <<<$'x\ny\nz' 5<&0 <<<$'7\n8\n9\n10\n11\n12' 6<&0

# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:

Olmasına bağlı olarak Demek readNalır -l, çıktı ya olduğu

a 1 x 7
b 2 y 8
c 3 z 9
d 4 10
5 11
12

veya

a 1 x 7
b 2 y 8
c 3 z 9

Her şeyi birden çok diziye kaydetmeden bir döngüde birden çok akışı okumak zorunda olmak, o kadar yaygın değildir. Sadece dizileri okumak istiyorsanız, bir göz atmalısınız mapfile.


||benim için çalışmıyor. Bu süreç dosya1 sonra dosya2 , ancak senkronize dosyaları tutmak yapar &&çıkar, süre ilk sırasında döngü eof . - GNU bash 4.1.5
Peter.O

Test etmek için boş bir anım yoktu - evet muhtemelen her iki elementi de yineleme istiyorsunuz. ||"Liste" operatör çoğu dilde gibi kısa devre edilir. Şüphesiz bu daha önce binlerce kez cevaplanmıştır ...
ormaaj

Mesele, daha önce bir şeyin ne sıklıkta bahsedildiğinin sıklığı değildir. Aksine, cevabınızda şu anda gösterdiğiniz kod çalışmıyor . " Muhtemelen her iki öğeyi de isteyin ..", hala test etmediniz gibi geliyor.
Peter.O

@ Peter.O Farkındayım. Düzeltildi.
ormaaj

2

Do-yapılır; yapılır - iki forma ve iki dona ihtiyacınız vardır:

for i in $(< file1); do for j in $(< file2); do echo $i $j;done ; done

Dosya2, elbette, dosya1'deki her kelime için bir kez işlenir.

Çoğu durumda kedinin yerine <kullanılması, olağanüstü bir performans kazancı olmalıdır. Alt süreç yok. (Son cümleden emin değilim).

sıkıştırılmamış:

for i in $(< file1)
do
  for j in $(< file2)
  do
    echo $i $j
  done
done

Kelimeleri okumaktan hoşlanmıyorsanız, satırları okuyup boşlukları korumak istiyorsanız, while öğesini kullanın ve okuyun:

while read line; do while read second; do echo "${line}${second}"; done <file2 ; done <file1

2 dosya eklemek istiyorsanız ve bunları iç içe yerleştirmek istemiyorsanız:

cat file1 file2 | while read line; do echo "${line}"; done

2 dosyayı sıkıştırmak istiyorsanız yapıştırmayı kullanın:

paste file1 file2

1
Alt süreç yok diyorsunuz. () Bir alt kabuk değil mi? Pendantik olmak istemiyorum ama şu anda neler olduğundan emin değilim. Mesela bir betiğim olduğunda ve örneğin şu anki kabuğu cd'lerle yok etmesini istemediğimde bunu bir () içinde yapıyorum. Ayrıca çalışan (uyku 4) ve örneğin bir ps takip bana süreç hala çalışıyor gösterir. Lütfen biraz açar mısınız?
Silverrocker

Üzgünüm, burada temelsiz hareket ediyorum. Duyduğumu sanıyordum, ama belki sadece <kıyaslanmış cat. Ve nasıl test edileceğinden ve ne zaman anlamsal olarak önemli olacağından emin değilim. Borular çağrılırsa, alt süreçlerdeki değişkenler ana sürece kadar kabarmaz, ancak burada değişkenler yoktur. Birisi açıklığa kavuşturana kadar eleştirel cümleye çarparım.
kullanıcı bilinmiyor

0

whileinteresitng sözdizimine sahiptir. Döngüden önce birden fazla komut koyabilirsiniz do ... whileve söz konusu durumun, aşağıdaki özel gereksinimlerinize bağlı olarak bu özelliği dengelemesi gerekebilir: en uzun dosyanın sonuna veya yalnızca en kısa dosyanın sonuna mı okuyorsunuz?

Örneğin, read || readsadece değil çalışır birinci dosyanın salt olduğunda becase, (Sorunun hükümlerine uygun olarak) true, ikinci dosyanın okuma edilir atlanır ilk dosya baştan sona okuma kadar Sonra ... statüsüne çünkü ise true, while döngüsü devam eder ve ikinci dosyayı baştan sona okur.

read && readyalnızca en kısa dosyaya kadar okumak istiyorsanız, dosyaları eşzamanlı olarak (eşzamanlı olarak) okuyacaktır. Ancak, her iki dosyayı da okumak istiyorsanız eof, while'ssözdizimi gereksinimleriyle çalışmanız gerekir . komutu ile hemen öncedo while , sıfır olmayan bir dönüş kodu üreten döngüsü sırasında döngünün patlak.

İşte her iki dosya nasıl okunacağı bir örnektir eof

while IFS= read -r line3 <&3 || ((eof3=1))
      IFS= read -r line4 <&4 || ((eof4=1))
      !((eof3 & eof4))
do
    echo "$line3, $line4"
done 3<file3 4<file4

(okumadan önce eof3 ve eof4'ü test etmek isteyebilirsiniz, ancak genel fikir, özellikle son doğru / yanlış durumda.

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.