Hãy xem xét một ví dụ, với một số văn bản đầu vào được làm cẩn thận:
text=' hello world\
foo\bar'
Đó là hai dòng, bắt đầu bằng một khoảng trắng và kết thúc bằng dấu gạch chéo ngược. Trước tiên, hãy xem xét những gì xảy ra mà không có bất kỳ biện pháp phòng ngừa nào xung quanh read
(nhưng sử dụng printf '%s\n' "$text"
để in cẩn thận $text
mà không có bất kỳ rủi ro mở rộng nào). (Dưới đây, $
là dấu nhắc shell.)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
read
ăn các dấu gạch chéo ngược: dấu gạch chéo ngược-dòng mới làm cho dòng mới bị bỏ qua và dấu gạch chéo ngược - bất cứ điều gì bỏ qua dấu gạch chéo ngược đầu tiên. Để tránh dấu gạch chéo ngược được xử lý đặc biệt, chúng tôi sử dụng read -r
.
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
Điều đó tốt hơn, chúng tôi có hai dòng như mong đợi. Hai dòng gần như chứa nội dung mong muốn: khoảng trắng kép giữa hello
và world
đã được giữ lại, vì nó nằm trong line
biến. Mặt khác, không gian ban đầu đã bị ăn hết. Đó là bởi vì read
đọc càng nhiều từ khi bạn chuyển các biến đó, ngoại trừ biến cuối cùng chứa phần còn lại của dòng - nhưng nó vẫn bắt đầu bằng từ đầu tiên, tức là các khoảng trắng ban đầu bị loại bỏ.
Vì vậy, để đọc từng dòng theo nghĩa đen, chúng ta cần đảm bảo rằng không có sự phân tách từ nào đang diễn ra. Chúng tôi làm điều này bằng cách đặt IFS
biến thành một giá trị trống.
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
Lưu ý cách chúng tôi đặt IFS
cụ thể cho thời lượng tích read
hợp . Biến IFS= read -r line
thiết lập biến môi trường IFS
(thành một giá trị trống) đặc biệt để thực hiện read
. Đây là một ví dụ của cú pháp lệnh đơn giản chung : một chuỗi (có thể trống) các phép gán biến được theo sau bởi một tên lệnh và các đối số của nó (ngoài ra, bạn có thể ném vào các chuyển hướng tại bất kỳ điểm nào). Vì read
được tích hợp sẵn, biến không bao giờ thực sự kết thúc trong môi trường của quy trình bên ngoài; dù sao giá trị $IFS
là những gì chúng ta chỉ định ở đó miễn read
là thực thi. Lưu ý rằng đó read
không phải là một tích hợp đặc biệt , vì vậy việc chuyển nhượng chỉ kéo dài trong suốt thời gian của nó.
Do đó, chúng tôi chú ý không thay đổi giá trị của IFS
các hướng dẫn khác có thể dựa vào nó. Mã này sẽ hoạt động bất kể mã xung quanh đã được đặt thành IFS
gì ban đầu và nó sẽ không gây ra bất kỳ rắc rối nào nếu mã bên trong vòng lặp phụ thuộc vào IFS
.
Tương phản với đoạn mã này, tìm kiếm các tệp trong một đường dẫn được phân tách bằng dấu hai chấm. Danh sách tên tệp được đọc từ một tệp, một tên tệp trên mỗi dòng.
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
Nếu vòng lặp là while IFS=; read -r name; do …
, thì for dir in $PATH
sẽ không tách $PATH
thành các thành phần được phân tách bằng dấu hai chấm. Nếu mã là IFS=; while read …
, nó sẽ rõ ràng hơn nữa mà IFS
không được đặt :
trong thân vòng lặp.
Tất nhiên, nó sẽ có thể khôi phục giá trị IFS
sau khi thực hiện read
. Nhưng điều đó đòi hỏi phải biết giá trị trước đó, đó là nỗ lực thêm. IFS= read
là cách đơn giản (và, thuận tiện, cũng là cách ngắn nhất).
¹ Và, nếu read
bị gián đoạn bởi một tín hiệu bị mắc kẹt, có thể trong khi bẫy được thực hiện - điều này không được xác định bởi POSIX và phụ thuộc vào vỏ trong thực tế.
while IFS=X read
không phân chia lúcX
, nhưngwhile IFS=X; read
...