Niektórzy ludzie mają błędne pojęcie, którym readjest polecenie odczytania wiersza. To nie jest.
readodczytuje słowa z (ewentualnie kontynuowanego odwrotnego ukośnika) wiersza, w którym słowa są $IFSrozdzielane, 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
readodczytuje 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 cby 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.
-rOpcja usuwa przetwarzanie ukośnika. Więc to samo polecenie powyżej z -rzamiast 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", "", baroraz ""(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 fooi 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 wordna wejściu jak foo:) z powłokami POSIX (nie ma zshniektórych pdkshwersji), to dane wejściowe jest uważane za jedno foosłowo, ponieważ w tych powłokach znaki $IFSsą uważane za terminatory , więc wordbędą zawierać foo, a nie foo:.
Zatem kanonicznym sposobem odczytu jednego wiersza danych wejściowych za pomocą readwbudowanego jest:
IFS= read -r line
(zauważ, że w przypadku większości readimplementacji działa to tylko w przypadku wierszy tekstu, ponieważ znak NUL nie jest obsługiwany, z wyjątkiem in zsh).
Korzystanie ze var=value cmdskładni powoduje, że IFSczas trwania tej cmdkomendy jest ustawiony inaczej .
Notatka historyczna
readWbudowany 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 readnie obsługiwała -ropcji (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 cna wejściu jak foo::barbyłoby przypisać bardo $b, a nie pusty ciąg.
W powłoce Bourne'a z:
var=value cmd
Jeśli cmdjest wbudowany (jak readjest), varpozostaje ustawiony na valuepo cmdzakończeniu. Jest to szczególnie ważne, $IFSponieważ w powłoce Bourne'a $IFSsłuży do dzielenia wszystkiego, nie tylko rozszerzeń. Ponadto, jeśli usuniesz znak spacji z $IFSpowł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 < filelub exec 3< file; read var <&3nie działały), więc w powłoce Bourne'a rzadko było używane readdo 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ą linepolecenie 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 1z 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.