Dlaczego muszę umieścić polecenie odczytu w podpowłoce podczas korzystania z potoku [duplikat]
Polecenie df .
może nam pokazać, na jakim urządzeniu jesteśmy. Na przykład,
me@ubuntu1804:~$ df .
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdb1 61664044 8510340 49991644 15% /home
Teraz chcę dostać ciąg /dev/sdb1
.
Próbowałem w ten sposób, ale nie zadziałało: df . | read a; read a b; echo "$a"
to polecenie dało mi puste wyjście. Ale df . | (read a; read a b; echo "$a")
będzie działać zgodnie z oczekiwaniami.
Jestem teraz trochę zdezorientowany.
Wiem, że (read a; read a b; echo "$a")
to podpowłoka, ale nie wiem, dlaczego muszę tutaj tworzyć podpowłokę. Jak rozumiem, x|y
przekieruje dane wyjściowe x
do wejścia y
. Dlaczego read a; read a b; echo $a
nie można uzyskać danych wejściowych, a podpowłoka może?
Odpowiedzi
Głównym problemem jest tutaj prawidłowe grupowanie poleceń. Podpowłoki to kwestia drugorzędna.
x|y
przekieruje wyjście zx
do wejściay
Tak, ale x | y; z
nie przekierowuje danych wyjściowych x
do obu y
i z
.
W df . | read a; read a b; echo "$a"
rurociąg tylko łączy df .
i read a
, inne komendy mają żadnego związku z tym rurociągu. Musisz zgrupować je read
razem: df . | { read a; read a b; }
lub df . | (read a; read a b)
aby rurociąg był podłączony do obu z nich.
Jednak teraz pojawia się problem podpowłoki: polecenia w potoku są uruchamiane w podpowłoce, więc ustawienie w nich zmiennej nie wpływa na powłokę nadrzędną. Więc echo
polecenie musi znajdować się w tej samej podpowłoce co read
s. Tak: df . | { read a; read a b; echo "$a"; }
.
Teraz, czy używasz, ( ... )
czy { ...; }
nie ma tutaj szczególnej różnicy, ponieważ polecenia w potoku są i tak uruchamiane w podpowłokach.
Alternatywą jest użycie zastępowania procesu :
{ read header; read filesystem rest; } < <(df .)
echo "$filesystem"
<(...)
Podstawienie proces wykonuje zawartej skrypt (w podpowłoce), ale działa jak nazwa pliku, więc trzeba pierwszym <
przekierować zawartość (który jest wyjście skryptu) do usztywnione skryptu. Zgrupowane polecenia są wykonywane w bieżącym shel ;.
Uzyskanie takiej czytelności może być trudne, ale możesz wstawić dowolne białe spacje w nawiasy klamrowe i podstawienie procesu.
{
read header
read filesystem rest
} < <(
df .
)
echo "$filesystem"
I może być łatwiej użyć zewnętrznego narzędzia do wyodrębnienia systemu plików:
filesystem=$( df . | awk 'NR == 2 {print $1}' )
Twoja pierwsza komenda
df . | read a; read a b; echo "$a"
jest skutecznie interpretowany jako
( df . | read a ) ; read a b; echo "$a"
Tak więc potok zasila tylko read a
polecenie.
Ponieważ potrzebujesz wielu odczytów z potoku, musisz zgrupować polecenia razem.
Teraz nie musi to być podpowłoka; może to być grupa…
bash-4.2$ df | { read a ; read a b ; echo $a ; }
devtmpfs
Częściej możesz potrzebować pętli
bash-4.2$ df | while read a > do > read a b > echo $a
> done
devtmpfs
tmpfs
/dev/vda3
/dev/vdb
Istnieje dodatkowy problem z bash
i po prawej stronie potoku uruchamianego w podpowłoce, więc $a
$b
wartości nie są dostępne poza while
pętlą, ale to inny problem!