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.