w bash, czytane po tym, jak potok nie ustawia wartości


22

Edycja: oryginalny tytuł brzmiał: „odczyt kończy się niepowodzeniem”

W ksh używam read jako wygodnego sposobu na rozdzielanie wartości:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

Ale kończy się niepowodzeniem:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

Nie znalazłem powodu na stronie podręcznika, dlaczego to się nie udaje, jakiś pomysł?


2
Jest to omówione (nieco niejasno) na stronie 024 często zadawanych pytań Grega dotyczących Bash .
— Scott

Odpowiedzi:


27

bash uruchamia prawą stronę potoku w kontekście podpowłoki , więc zmiany zmiennych (co się readdzieje) nie są zachowywane - umierają, gdy podpowłoka robi to, na końcu polecenia.

Zamiast tego możesz użyć podstawienia procesu :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

W tym przypadku readdziała w naszej podstawowej powłoce, a nasze polecenie generujące dane wyjściowe działa w podpowłoce. <(...)Składnia tworzy podpowłoce i łączy swoje wyjście do rury, którą przekierowania na wejście readze zwykłej <eksploatacji . Ponieważ readuruchomiono w naszej głównej powłoce, zmienne są ustawione poprawnie.

Jak wskazano w komentarzu, jeśli Twoim celem jest dosłowne podzielenie łańcucha na zmienne, możesz użyć łańcucha tutaj :

read a b dump <<<"1 2 3 4 5"

Zakładam, że jest w tym coś więcej, ale jest to lepsza opcja, jeśli nie ma.


3
Lub nawet read a b dump <<< '1 2 3 4 5'.
— choroba

Dziękuję wszystkim, przy okazji zauważyłem, że mksh (na cygwin) robi to samo co bash.
— Emmanuel

@Michael Homer Dobre wyjaśnienie! Znalazłem inny jeden przykład, który może wyjaśnić, że każda komenda w rurociągu uruchomić we własnej podpowłoce: cat /etc/passwd | (read -r line ; echo $line). Ale obok echoz $linektórym nie jest w rurociągu put nic na ekranie, ponieważ wartość została istniał tylko w nawiasie (podpowłoki). Mam nadzieję, że to komuś pomaga.
— Yurij Goncharuk

17

Nie jest to bashbłąd, ponieważ POSIXpozwala zarówno na zachowanie, jak bashi kshzachowanie, co prowadzi do niefortunnej rozbieżności, którą obserwujesz.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12

Ponadto każde polecenie potoku z wieloma poleceniami znajduje się w środowisku podpowłoki; jednak jako rozszerzenie dowolne lub wszystkie polecenia w potoku mogą być wykonywane w bieżącym środowisku. Wszystkie pozostałe polecenia będą wykonywane w bieżącym środowisku powłoki.

Jednak za pomocą bash 4.2i nowszych można ustawić lastpipeopcję w nieinteraktywnych skryptach, aby uzyskać oczekiwany wynik, np .:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

Wynik:

before:
after: 2 1

1
+1 dziękuję za informację „ostatnia rura”. przepraszam za opóźnienie
— Emmanuel

1
Problem lastpipepolega na tym, że nie działa on w innych powłokach (np. myślnik). w zasadzie nie ma sposobu, aby zrobić to przenośne, nie uruchamiając wszystkiego w tej podpowłoce, patrz stackoverflow.com/questions/36268479/…
— anarcat

1
@anarcat To prawda, ale pytanie tutaj dotyczyło bash.
— jlliagre

@anarcat: Może się to zmienić w przyszłości, ponieważ istnieje potrzeba zmiany POSIX z innego powodu (Status PIPE) patrz tutaj: unix.stackexchange.com/questions/476834/… Zmiana innych powłok nie jest trywialna, zajęło mi to kilka miesięcy przepisać parser i interpeter na powłoce Bourne'a (bosh), aby zaimplementować szybsze zachowanie współczesnego ksh.
— schily
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.