Spójrzmy na przykład z starannie przygotowanym tekstem wejściowym:
text=' hello world\
foo\bar'
To dwie linie, pierwsza zaczynająca się spacją i kończąca się odwrotnym ukośnikiem. Po pierwsze, spójrzmy na to, co się dzieje bez żadnych środków ostrożności read(ale używając printf '%s\n' "$text"do ostrożnego drukowania $textbez ryzyka ekspansji). (Poniżej $ znajduje się monit powłoki).
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
readzjadł ukośniki odwrotne: ukośnik-nowa linia powoduje zignorowanie nowej linii, a ukośnik-cokolwiek ignoruje ten pierwszy ukośnik. Aby uniknąć specjalnego traktowania ukośników odwrotnych, używamy read -r.
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
Tak lepiej, mamy dwie linie zgodnie z oczekiwaniami. Dwie linie prawie zawierają pożądaną treść: podwójna spacja między helloi worldzostała zachowana, ponieważ znajduje się w linezmiennej. Z drugiej strony początkowa przestrzeń została zjedzona. Dzieje się tak, ponieważ readodczytuje tyle słów, ile przekazujesz, zmienne, z tą różnicą, że ostatnia zmienna zawiera resztę wiersza - ale wciąż zaczyna się od pierwszego słowa, tzn. Początkowe spacje są odrzucane.
Tak więc, aby odczytać każdą linię dosłownie, musimy upewnić się, że nie dochodzi do podziału słów . Robimy to, ustawiając IFSzmienną na pustą wartość.
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
Zwróć uwagę, jak ustawiliśmy IFS specjalnie na czas trwania readwbudowanego . W IFS= read -r lineustawia zmienne środowiska IFS(na pusty wartość) specjalnie dla realizacji read. Jest to przykład ogólnej składni komend prostych : (być może pusta) sekwencja przypisań zmiennych, po której następuje nazwa komendy i jej argumenty (można także przekierowywać w dowolnym momencie). Ponieważ readjest to funkcja wbudowana, zmienna nigdy nie kończy się w środowisku zewnętrznego procesu; niemniej jednak wartość $IFSjest przypisywana tak długo, jak długo readjest wykonywana¹. Pamiętaj, że readnie jest to specjalne wbudowane , więc zadanie trwa tylko przez czas jego trwania.
Dlatego staramy się nie zmieniać wartości IFSinnych instrukcji, które mogą na nim polegać. Ten kod będzie działał bez względu na to, co IFSpoczątkowo ustawił otaczający kod i nie spowoduje żadnych problemów, jeśli kod w pętli będzie polegał IFS.
Porównaj z tym fragmentem kodu, który wyszukuje pliki w ścieżce oddzielonej dwukropkami. Lista nazw plików jest odczytywana z pliku, jedna nazwa pliku w wierszu.
IFS=":"; set -f
while IFS= read -r name; do
for dir in $PATH; do
## At this point, "$IFS" is still ":"
if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
done
done <filenames.txt
Jeśli pętla była while IFS=; read -r name; do …, to for dir in $PATHnie podzieliłaby się $PATHna składniki oddzielone dwukropkami. Gdyby kod był IFS=; while read …, byłoby jeszcze bardziej oczywiste, że IFSnie jest ustawione :w treści pętli.
Oczywiście możliwe byłoby przywrócenie wartości IFSpo wykonaniu read. Wymagałoby to jednak znajomości poprzedniej wartości, która stanowi dodatkowy wysiłek. IFS= readto prosty sposób (i dogodnie także najkrótszy sposób).
¹ A jeśli readzostanie przerwany przez sygnał pułapki, być może podczas działania pułapki - nie jest to określone przez POSIX i zależy od powłoki w praktyce.
while IFS=X readnie dzieli się naX, alewhile IFS=X; read...