Rozumiem oczywiście, że można dodać wartość do zmiennej separatora pól wewnętrznych. Na przykład:
$ IFS=blah
$ echo "$IFS"
blah
$
Rozumiem również, że read -r line
zapisze dane stdin
w zmiennej o nazwie line
:
$ read -r line <<< blah
$ echo "$line"
blah
$
Jak jednak polecenie może przypisać wartość zmiennej? I czy najpierw przechowuje dane od stdin
do zmiennej, line
a następnie podaje wartość line
do IFS
?
bash
shell-script
Jaskółka oknówka
źródło
źródło
Odpowiedzi:
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
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:
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 jestread 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ątkiemread -a
). Podczas gdy jeśli zastąpimy to:
spacją, dzielenie odbywa się tylko nafoo
ibar
. 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=::
lubIFS=' '
).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 jakfoo:
) z powłokami POSIX (nie mazsh
niektórychpdksh
wersji), to dane wejściowe jest uważane za jednofoo
słowo, ponieważ w tych powłokach znaki$IFS
są uważane za terminatory , więcword
będą zawieraćfoo
, a niefoo:
.Zatem kanonicznym sposobem odczytu jednego wiersza danych wejściowych za pomocą
read
wbudowanego jest:(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 inzsh
).Korzystanie ze
var=value cmd
składni powoduje, żeIFS
czas trwania tejcmd
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ś takimsed '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 jakfoo::bar
byłoby przypisaćbar
do$b
, a nie pusty ciąg.W powłoce Bourne'a z:
Jeśli
cmd
jest wbudowany (jakread
jest),var
pozostaje ustawiony navalue
pocmd
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
lubexec 3< file; read var <&3
nie działały), więc w powłoce Bourne'a rzadko było używaneread
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: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.źródło
sh
różnicami jest również przydatny do pisania przenośnych skryptów!)bash-4.4.19
,while read -r; do echo "'$REPLY'"; done
działa jakwhile IFS= read -r line; do echo "'$line'"; done
.read
linii do odczytu linii jest błędne, musi istnieć coś innego. Czym może być to błędne pojęcie? Czy też to pierwsze stwierdzenie jest technicznie poprawne, ale tak naprawdę błędnym pojęciem jest: „read to polecenie do czytania słów z wiersza. Ponieważ jest tak potężne, możesz użyć go do odczytu wierszy z pliku, wykonując:IFS= read -r line
”Teoria
Istnieją tutaj dwie koncepcje:
IFS
to Separator pól wejściowych, co oznacza, że odczytany ciąg zostanie podzielony na podstawie znaków wIFS
. W wierszu poleceniaIFS
zwykle są dowolne znaki spacji, dlatego wiersz poleceń dzieli się na spacje.VAR=value command
oznacza „zmodyfikuj środowisko poleceń, abyVAR
miało wartośćvalue
”. Zasadniczo poleceniecommand
będzieVAR
miało wartośćvalue
, ale każde polecenie wykonane po tym będzie nadalVAR
miało poprzednią wartość. Innymi słowy, zmienna ta zostanie zmodyfikowana tylko dla tej instrukcji.W tym przypadku
Tak więc, robiąc
IFS= read -r line
, ustawiaszIFS
pusty ciąg znaków (żaden znak nie zostanie użyty do podziału, dlatego nie nastąpi podział), abyread
odczytać cały wiersz i zobaczyć go jako jedno słowo, które zostanie przypisane doline
zmiennej. Zmiany mająIFS
wpływ tylko na tę instrukcję, więc zmiana nie będzie miała wpływu na następujące polecenia.Na marginesie
Chociaż polecenie jest prawidłowe i będzie działać zgodnie z przeznaczeniem, ustawienie
IFS
w tym przypadkunie jestkonieczne 1 może nie być konieczne. Jak napisano nabash
stronie man weread
wbudowanej sekcji:Ponieważ masz tylko
line
zmienną, każde słowo i tak zostanie do niej przypisane, więc jeśli nie potrzebujesz żadnego z poprzedzających i końcowych białych znaków 1, możesz po prostu napisaćread -r line
i gotowe.[1] Jako przykład tego, w jaki sposób wartość
unset
domyślna$IFS
spowoduje, że spacja będzieread
miała początkowy / końcowy biały znak IFS , możesz spróbować:Uruchom go, a zobaczysz, że poprzednie i końcowe postacie nie przetrwają, jeśli
IFS
nie zostanie rozbrojone. Co więcej, mogłyby się zdarzyć dziwne rzeczy, gdyby$IFS
zostały zmodyfikowane gdzieś wcześniej w skrypcie.źródło
Należy przeczytać, że oświadczenie w dwóch częściach, pierwsza kasuje wartość zmiennej IFS, czyli jest odpowiednikiem bardziej czytelny
IFS=""
, drugi czytaline
zmienną z stdinread -r line
.Specyficzne w tej składni jest to, że wpływ na IFS jest przemijający i ważny tylko dla
read
polecenia.O ile mi czegoś nie brakuje, w tym konkretnym przypadku kasowanieIFS
nie ma żadnego efektu, ponieważ cokolwiekIFS
jest ustawione, cała linia zostanie odczytana wline
zmiennej. Nastąpiłaby zmiana zachowania tylko w przypadku, gdy więcej niż jedna zmienna została przekazana jako parametr doread
instrukcji.Edytować:
Ma
-r
to na celu umożliwienie\
specjalnego przetwarzania zakończenia, którego nie można przetwarzać, tzn. Aby odwrotny ukośnik został uwzględniony wline
zmiennej, a nie jako znak kontynuacji umożliwiający wprowadzanie wielu wierszy.Wyczyszczenie IFS powoduje efekt uboczny polegający na zapobieganiu czytaniu w celu przycięcia potencjalnych początkowych i końcowych znaków spacji lub tabulatorów, np .:
Dzięki Rici za wskazanie tej różnicy.
źródło
read -r line
przycina początkowe i końcowe białe spacje przed przypisaniem danych wejściowych doline
zmiennej.IFS= read a b <<< 'aa bb' ; echo "-$a-$b-"
pokaże-aa bb--