Używanie „podczas odczytu…” w skrypcie linux

34

Czy ktoś mógłby wyjaśnić, jak działa następujący kod?

echo '1 2 3 4 5 6' | while read a b c
do
  echo $c $b $a
done

W szczególności chciałbym wiedzieć, dlaczego wyjście tej pętli jest 3 4 5 6 2 1zamiast 3 2 1i 6 5 4na dwóch osobnych liniach? Nie mogę się tym zająć ...

linuxgringo
źródło

Odpowiedzi:

41

readodczytuje całą linię ze standardowego wejścia, dzieli linię na pola i przypisuje te pola do podanych zmiennych. Jeśli jest więcej elementów niż zmiennych, pozostałe elementy są przypisywane do ostatniej zmiennej.

W twoim przypadku $ajest przydzielony 1, $bjest przypisany, 2a $cpozostałe 3 4 5 6.

Florian Diesch
źródło
Dzięki Florian! Teraz ma to sens ... Z jakiegoś powodu myślałem, że spacje ograniczają odczyt każdej zmiennej, ale najwyraźniej nie. Doceniam twoją pomoc!!
linuxgringo
24

Przepisanie pętli w ten sposób ujawnia, co się dzieje:

echo '1 2 3 4 5 6' | while read a b c
  do
    echo '(iteration beginning)' a="$a" b="$b" c="$c" '(iteration ending)'
  done

Daje to jako wynik:

(iteration beginning) a=1 b=2 c=3 4 5 6 (iteration ending)

Zauważ najpierw, że uruchamiane jest tylko jedno polecenie echa. Gdyby został uruchomiony więcej niż jeden raz, zobaczyłbyś między innymi napisy (iteration beginning)i (iteration ending)napisy wydrukowane więcej niż jeden raz.

To znaczy, że whilepętla tutaj tak naprawdę niczego nie osiąga. readWbudowane odczytuje białymi oddzielone tekst 1 do każdej zmiennej określonej. Dodatkowe dane wejściowe są dołączane na końcu ostatniej określonej zmiennej. 2 W ten sposób zmienne ai bodbioru o wartości 1i 2, odpowiednio, a cprzyjmuje wartości 3 4 5 6.

Gdy warunek pętli ( while read a b c) jest oceniany po raz drugi, nie ma już żadnych danych wejściowych z potoku (przesłaliśmy do niego tylko jeden wiersz tekstu), więc readpolecenie ocenia na fałsz zamiast prawdy, a pętla zatrzymuje się (przed wykonaniem polecenia ciało po raz drugi).

1 : Aby mieć charakter techniczny i konkretne, wbudowane , gdy przeszedł nazw zmiennych jako argumenty, odczytuje wejścia, dzieląc go na odrębne „słów” , gdy napotka IFS spacje (patrz również na to pytanie i ten artykuł ).read

2 : readzachowanie polegające na blokowaniu jakichkolwiek dodatkowych pól danych wejściowych w ostatniej określonej zmiennej jest początkowo nieintuicyjne dla wielu skrypterów. Łatwiej jest to zrozumieć, gdy weźmie się pod uwagę, że, jak mówi odpowiedź Floriana Diescha , readzawsze (spróbuje) przeczytać całą linię - i readma to być użyteczne zarówno z pętlą, jak i bez niej.

Eliah Kagan
źródło
Eliah, dziękuję za poświęcenie czasu na wyjaśnienie wszystkich szczegółów. Podejrzewałem, że whilew tym przykładzie nie spełniło to normalnego celu, ale potem readpolecenie mnie wyrzuciło ... W jakiś sposób zinterpretowałem to jako „choć read a b cnie jest to fałsz, zrób echo ...”. Dziękujemy za wyjaśnienie, jak to naprawdę działało. Wczoraj natknąłem się na ten kod i wiedziałem, że to mnie zaskoczy, dopóki go nie rozgryzłem ... lol
linuxgringo
@linuxgringo W rzeczywistości treść pętli jest wykonywana za każdym razem read a b c, gdy zostanie ustawiona wartość true, a warunek pętli ( read a b c) działa więcej niż jeden raz. Bit, który ocenia jako prawdziwy tylko za pierwszym razem. Za drugim razem nie ma więcej danych do odczytu z potoku, więc napotyka się koniec pliku , co powoduje readzwrócenie wartości false . (Szczegółowe informacje można znaleźć w ostatniej sekcji wyniku help read„Statusu wyjścia”, zauważając, że w skrypcie powłoki zero oznacza prawda, a niezerowe oznacza fałsz.) Jeśli potokowałeś więcej niż jeden wiersz danych wejściowych while read ..., treść pętli zostać wykonanym wiele razy.
Eliah Kagan