Dlaczego otrzymuję różne wartości $x
z poniższych fragmentów?
#!/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?
Odpowiedzi:
Właściwe wyjaśnienie zostało już podane przez jsbillings i geekozaura , ale pozwólcie, że rozwinę się nieco.
W większości powłok, w tym bash, każda strona potoku działa w podpowłoce, więc wszelkie zmiany stanu wewnętrznego powłoki (takie jak ustawianie zmiennych) pozostają ograniczone do tego segmentu potoku. Jedyną informacją, którą można uzyskać z podpowłoki, jest to, co wyprowadza (do standardowego wyjścia i innych deskryptorów plików) i jej kod wyjścia (który jest liczbą od 0 do 255). Na przykład następujący fragment drukuje 0:
W ksh (warianty pochodzące z kodu AT&T, a nie warianty pdksh / mksh) i zsh, ostatni element w potoku jest wykonywany w powłoce nadrzędnej. (POSIX pozwala na oba zachowania.) Tak więc powyższy fragment drukuje 2.
Przydatnym idiomem jest uwzględnienie w potoku kontynuacji pętli while (lub cokolwiek, co masz po prawej stronie potoku, ale pętla while jest tutaj powszechna):
źródło
< <(locate -ber ^\.tag$)
, dzięki oryginalnej, nieco niejasnej odpowiedzi i komentacjom geekozaura i glenna jackmana. Początkowo miałem dylemat, czy przyjąć odpowiedź, ale wynik netto było dość jasne, szczególnie z komentarzem uzupełniającymMasz problem ze zmiennym zakresem. Zmienne zdefiniowane w pętli while znajdującej się po prawej stronie potoku mają swój własny kontekst zakresu lokalnego, a zmiany w zmiennej nie będą widoczne poza pętlą. Pętla while jest w zasadzie podpowłoką, która otrzymuje KOPIĘ środowiska powłoki, a wszelkie zmiany w środowisku są tracone na końcu powłoki. Zobacz to pytanie StackOverflow .
ZAKTUALIZOWANO : Zlekceważyłem wskazując na ważny fakt, że pętla while z własną podpowłoką była spowodowana tym, że była punktem końcowym potoku, zaktualizowałem to w odpowiedzi.
źródło
while
Pętla działa jako tylny koniec potoku, który wrzuca ją do podpowłoki.blah|blah|while read ...
możesz miećwhile read ...; done < <(blah|blah)
Jak wspomniano w innych odpowiedziach , części potoku działają w podpowłokach, więc wprowadzone tam modyfikacje nie są widoczne dla głównej powłoki.
Jeśli weźmiemy pod uwagę tylko Bash, oprócz
cmd | { stuff; more stuff; }
struktury istnieją dwa inne obejścia :Przekieruj dane wejściowe z podstawienia procesu :
Dane wyjściowe z komendy in
<(...)
wyglądają tak, jakby były nazwanymi potokami.lastpipe
Opcja, która sprawia, że praca atakujących jak ksh i uruchamia ostatnią część rurociągu w głównym procesie powłoki. Chociaż działa to tylko wtedy, gdy kontrola zadań jest wyłączona, tj. Nie w interaktywnej powłoce:lub
Podstawianie procesów jest oczywiście obsługiwane także w ksh i zsh. Ale ponieważ i tak uruchamiają ostatnią część potoku w głównej powłoce, użycie go jako obejścia nie jest tak naprawdę konieczne.
źródło
to może działać.
źródło