Dlaczego muszę umieścić polecenie odczytu w podpowłoce podczas korzystania z potoku [duplikat]

Nov 30 2020

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|yprzekieruje dane wyjściowe xdo wejścia y. Dlaczego read a; read a b; echo $anie można uzyskać danych wejściowych, a podpowłoka może?

Odpowiedzi

8 muru Dec 01 2020 at 03:10

Głównym problemem jest tutaj prawidłowe grupowanie poleceń. Podpowłoki to kwestia drugorzędna.

x|yprzekieruje wyjście z xdo wejściay

Tak, ale x | y; znie przekierowuje danych wyjściowych xdo obu yi 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 readrazem: 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 echopolecenie musi znajdować się w tej samej podpowłoce co reads. 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.

3 glennjackman Dec 01 2020 at 03:10

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}' )
2 StephenHarris Dec 01 2020 at 03:11

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 apolecenie.

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 bashi po prawej stronie potoku uruchamianego w podpowłoce, więc $a $bwartości nie są dostępne poza whilepętlą, ale to inny problem!