Niektórzy ludzie mają błędne pojęcie, którym read
jest polecenie odczytania wiersza. To nie jest.
read
odczytuje słowa z (ewentualnie kontynuowanego odwrotnego ukośnika) wiersza, w którym słowa są $IFS
rozdzielane, a odwrotnego ukośnika można użyć do opuszczenia ograniczników (lub kontynuacji linii).
Ogólna składnia to:
read word1 word2... remaining_words
read
odczytuje stdin jeden bajt na raz, aż znajdzie Niecytowany znak nowej linii (lub końcówki wejściowe), dzieli, że według skomplikowanych zasad i zapisuje wynik tego dzielenia się $word1
, $word2
... $remaining_words
.
Na przykład na wejściu takim jak:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
i o wartości domyślnej $IFS
, read a b c
by przypisać:
$a
⇐ foo
$b
⇐ bar baz
$c
⇐ blah blahwhatever whatever
Teraz, jeśli przeszedł tylko jeden argument, to się nie stanie read line
. Nadal jest read remaining_words
. Przetwarzanie odwrotnego ukośnika jest nadal wykonywane, znaki białych znaków IFS są nadal usuwane od początku i końca.
-r
Opcja usuwa przetwarzanie ukośnika. Więc to samo polecenie powyżej z -r
zamiast tego przypisałoby
$a
⇐ foo
$b
⇐ bar\
$c
⇐ baz bl\ah blah\
Teraz, dla części dzielącej, ważne jest, aby zdać sobie sprawę, że istnieją dwie klasy znaków dla $IFS
: białych znaków IFS (tj. Spacja i tabulator (i nowa linia, choć tutaj nie ma to znaczenia, chyba że użyjesz -d), które również się zdarzają być w wartości domyślnej $IFS
) i innych. Traktowanie tych dwóch klas postaci jest inne.
Z IFS=:
( :
nie będąc biały znak IFS), wejście jak :foo::bar::
zostanie podzielony na ""
, "foo"
, ""
, bar
oraz ""
(i dodatkowy ""
z niektórych implementacjach jednak, że nie ma znaczenia, z wyjątkiem read -a
). Podczas gdy jeśli zastąpimy to :
spacją, dzielenie odbywa się tylko na foo
i bar
. To jest wiodące, a końcowe są ignorowane, a ich sekwencje są traktowane jak jeden. Istnieją dodatkowe zasady łączenia znaków spacji i spacji $IFS
. Niektóre implementacje mogą dodawać / usuwać specjalne traktowanie poprzez podwojenie znaków w IFS ( IFS=::
lub IFS=' '
).
Więc tutaj, jeśli nie chcemy, aby usuwane były wiodące i końcowe znaki bez białych znaków, należy usunąć te znaki IFS z białych znaków z IFS.
Nawet w przypadku znaków IFS spoza białymi znakami, jeśli wiersz wejściowy zawiera jeden (i tylko jeden) z tych znaków i jest to ostatni znak w wierszu (jak IFS=: read -r word
na wejściu jak foo:
) z powłokami POSIX (nie ma zsh
niektórych pdksh
wersji), to dane wejściowe jest uważane za jedno foo
słowo, ponieważ w tych powłokach znaki $IFS
są uważane za terminatory , więc word
będą zawierać foo
, a nie foo:
.
Zatem kanonicznym sposobem odczytu jednego wiersza danych wejściowych za pomocą read
wbudowanego jest:
IFS= read -r line
(zauważ, że w przypadku większości read
implementacji działa to tylko w przypadku wierszy tekstu, ponieważ znak NUL nie jest obsługiwany, z wyjątkiem in zsh
).
Korzystanie ze var=value cmd
składni powoduje, że IFS
czas trwania tej cmd
komendy jest ustawiony inaczej .
Notatka historyczna
read
Wbudowany został wprowadzony przez Bourne shell i był już czytać słowa , a nie linii. Istnieje kilka ważnych różnic w nowoczesnych powłokach POSIX.
Powłoka Bourne'a read
nie obsługiwała -r
opcji (która została wprowadzona przez powłokę Korna), więc nie ma sposobu, aby wyłączyć przetwarzanie odwrotnego ukośnika inaczej niż wstępne przetwarzanie danych wejściowych z czymś takim sed 's/\\/&&/g'
.
Powłoka Bourne'a nie miała pojęcia dwóch klas postaci (co ponownie zostało wprowadzone przez ksh). W Bourne Shell wszystkie znaki przechodzą takie samo traktowanie jak IFS znaków odstępu zrobić w ksh, czyli IFS=: read a b c
na wejściu jak foo::bar
byłoby przypisać bar
do $b
, a nie pusty ciąg.
W powłoce Bourne'a z:
var=value cmd
Jeśli cmd
jest wbudowany (jak read
jest), var
pozostaje ustawiony na value
po cmd
zakończeniu. Jest to szczególnie ważne, $IFS
ponieważ w powłoce Bourne'a $IFS
służy do dzielenia wszystkiego, nie tylko rozszerzeń. Ponadto, jeśli usuniesz znak spacji z $IFS
powłoki Bourne'a, "$@"
przestanie to działać.
W powłoce Bourne przekierowanie polecenia złożonego powoduje, że działa ono w podpowłoce (w najwcześniejszych wersjach nawet rzeczy takie jak read var < file
lub exec 3< file; read var <&3
nie działały), więc w powłoce Bourne'a rzadko było używane read
do niczego poza danymi wejściowymi użytkownika na terminalu (tam, gdzie miało to sens obsługa kontynuacji linii)
Niektóre Unices (jak HP / UX, jest też jeden w util-linux
) nadal mają line
polecenie odczytu jednego wiersza danych wejściowych (które były standardowym poleceniem UNIX aż do wersji specyfikacji Single UNIX wersja 2 ).
Jest to w zasadzie to samo, head -n 1
z wyjątkiem tego, że odczytuje jeden bajt na raz, aby upewnić się, że nie czyta więcej niż jednej linii. W tych systemach możesz:
line=`line`
Oczywiście oznacza to odrodzenie nowego procesu, wykonanie polecenia i odczytanie jego wyniku przez potok, czyli o wiele mniej wydajny niż ksh IFS= read -r line
, ale o wiele bardziej intuicyjny.