Değişkenim neden bir 'okurken' döngüsünde yerel, ancak başka görünüşte benzer bir döngüde değil?


25

$xAşağıdaki pasajlardan neden farklı değerler alıyorum ?

#!/bin/bash

x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x 
#    x=55 .. I'd expect this result

x=1
cat junk | while read var ; do x=55 ; done
echo x=$x 
#    x=1 .. but why?

x=1
echo fred | while read var ; do x=55 ; done
echo x=$x 
#    x=1  .. but why?

Yanıtlar:


26

Doğru açıklama jsbillings ve geekosaur tarafından zaten yapıldı , ama bu konuda biraz genişleyeyim.

Bash dahil çoğu kabukta, bir boru hattının her bir tarafı bir alt kabuk içerisinde çalışır, böylece kabuğun iç durumundaki herhangi bir değişiklik (ayar değişkenleri gibi) bir boru hattının bu segmentiyle sınırlı kalır. Bir alt kabuktan alabileceğiniz tek bilgi çıktılarını (standart çıktıya ve diğer dosya tanımlayıcılarına) ve çıkış kodunu (0 ile 255 arasında bir sayıdır). Örneğin, aşağıdaki kod parçası 0 yazdırır:

a=0; a=1 | a=2; echo $a

Ksh (AT&T kodundan türetilen, pdksh / mksh türevlerinden türetilen türevleri) ve zsh'da, bir boru hattındaki son öğe ana kabukta yürütülür. (POSIX, her iki davranışa da izin verir.) Yukarıdaki yukarıdaki snippet, 2 yazdırır.

Yararlı bir deyim, sürgünün devamını (veya boru hattının sağ tarafında ne varsa, ancak bir süre döngüsü aslında burada yaygındır) boru hattında eklemektir.

cat junk | {
  while read var ; do x=55 ; done
  echo x=$x 
}

1
Teşekkürler Gilles .. Bu bir = 0; a = 1 | a = 2 çok net bir görüntü veriyor .. ve sadece iç durumun yerelleştirilmesiyle değil, aynı zamanda bir boru hattının borudan bir şey göndermesi gerekmiyor (çıkış kodu (?) dışında). bir boruya ilginç bir bakış açısı var ... < <(locate -ber ^\.tag$)Orjinali net olmayan cevap ve geekosaur ve glenn jackman'ın anıları sayesinde senaryomun çalışmasını sağladım. Başlangıçta cevabı kabul etmekle ilgili bir ikilemdeydim ama nett sonucu oldukça açıktı, özellikle jsbillings'in izleyen yorumu ile :)
Peter.O

bir işlevin içine girmiş gibi hissediyorum, bu yüzden bazı değişkenleri ve testleri onun içine taşıdım ve harika çalıştı, thx!
Kova Gücü

8

Değişken kapsam sorunuyla karşılaşıyorsunuz. Borunun sağ tarafındaki while döngüsünde tanımlanan değişkenler kendi yerel kapsam içeriğine sahiptir ve değişkende yapılan değişiklikler döngünün dışında görülmez. While döngüsü esas olarak alır bir alt kabuk olan KOPYA kabuk çevre ve çevreye herhangi bir değişiklik kabuğun sonunda kaybolur. Bu StackOverflow sorusuna bakın .

GÜNCELLEME : Kendi alt kabuğuyla while döngüsünün bir borunun bitiş noktası olmasından kaynaklandığını vurgulamayı ihmal ettim, cevabını güncelledim.


@ jsbillings .. Tamam, bu son iki parçacığı açıklıyor, ancak döngüde ayarlanan $ x değerinin 55 olduğu (bir "süre" döngüsünün kapsamının ötesinde) olduğu gibi birincisini açıklamıyor
Peter.O

5
@ fred.bear: whileDöngüyü bir alt kabuğun içine fırlatan bir boru hattının kuyruk ucu olarak çalışıyor .
geekosaur

2
Bu, bash işlem ikamesinin devreye girdiği yerdir. Bunun yerine, blah|blah|while read ...olabilirwhile read ...; done < <(blah|blah)
glenn jackman

1
@geekosaur: Cevabımda yer almayı ihmal ettiğim detayları doldurduğunuz için teşekkürler.
jsbillings

1
-1 Üzgünüz, bu cevap sadece yanlış. Bu programın birçok programlama dilinde nasıl çalıştığını ancak kabukta olmadığını açıklar. @Gilles, aşağıda doğru.
jpc

6

Diğer cevaplarda belirtildiği gibi , bir boru hattının parçaları alt kabuklarda uzanır, bu nedenle yapılan değişiklikler ana kabuk tarafından görülmez.

Sadece Bash'i düşünürsek, cmd | { stuff; more stuff; }yapının yanı sıra diğer iki geçici çözüm vardır :

  1. Girdiyi işlem ikamesinden yönlendirin :

    while read var ; do x=55 ; done < <(echo fred)
    echo "$x"

    Komutun çıktısı, <(...)adlandırılmış bir kanalmış gibi görünmek için yapılır.

  2. lastpipeKsh gibi Bash iş yapar ve ana kabuk sürecinde boru hattının son kısmını çalıştırır seçeneği. Sadece iş kontrolü devre dışı bırakılmışsa, yani etkileşimli bir kabukta çalışmasa da:

    bash -c '
      shopt -s lastpipe
      echo fred | while read var ; do x=55 ; done; 
      echo "$x"
    '

    veya

    bash -O lastpipe -c '
      echo fred | while read var ; do x=55 ; done; 
      echo "$x"
    '

İşlem değiştirme elbette ksh ve zsh'de de desteklenir. Ancak, yine de ana hattaki boru hattının son kısmını çalıştırdıklarından, bunu geçici bir çözüm olarak kullanmak gerçekten gerekli değildir.


0
#!/bin/bash
set -x

# prepare test data.
mkdir -p ~/test_var_global
cd ~/test_var_global
echo "a"> core.1
echo "b"> core.2
echo "c"> core.3


var=0

coreFiles=$(find . -type f -name "core*")
while read -r file;
do
  # perform computations on $i
  ((var++))
done <<EOF
$coreFiles
EOF

echo $var

Result:
...
+ echo 3
3

işe yarayabilir.

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.