Một số người có quan niệm sai lầm đó readlà lệnh đọc một dòng. Không phải vậy.
readđọc các từ từ một dòng (có thể là dấu gạch chéo tiếp tục), trong đó các từ được $IFSphân cách và dấu gạch chéo ngược có thể được sử dụng để thoát các dấu phân cách (hoặc tiếp tục dòng).
Cú pháp chung là:
read word1 word2... remaining_words
readđọc stdin một byte tại một thời điểm cho đến khi nó tìm thấy một ký tự xuống dòng unescaped (hoặc end-of-input), chia tách mà theo quy định phức tạp và lưu trữ các kết quả của tách đó vào $word1, $word2... $remaining_words.
Ví dụ trên một đầu vào như:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
và với giá trị mặc định là $IFS, read a b csẽ gán:
$a ⇐ foo
$b ⇐ bar baz
$c ⇐ blah blahwhatever whatever
Bây giờ nếu chỉ thông qua một đối số, điều đó sẽ không trở thành read line. Nó vẫn vậy read remaining_words. Xử lý dấu gạch chéo ngược vẫn được thực hiện, các ký tự khoảng trắng IFS vẫn bị xóa từ đầu và cuối.
Các -rtùy chọn loại bỏ các xử lý dấu chéo ngược. Vì vậy, cùng một lệnh ở trên với -rthay vì gán
$a ⇐ foo
$b ⇐ bar\
$c ⇐ baz bl\ah blah\
Bây giờ, đối với phần tách, điều quan trọng là phải nhận ra rằng có hai loại ký tự cho $IFS: các ký tự khoảng trắng IFS (cụ thể là khoảng trắng và tab (và dòng mới, mặc dù ở đây không quan trọng trừ khi bạn sử dụng -d), điều này cũng xảy ra ở giá trị mặc định của $IFS) và các giá trị khác. Cách đối xử cho hai lớp nhân vật đó là khác nhau.
Với IFS=:( :là không phải là một nhân vật khoảng trắng IFS), một đầu vào như :foo::bar::sẽ được chia thành "", "foo", "", barvà ""(và thêm ""với một số hiện thực mặc dù điều đó không thành vấn đề trừ read -a). Trong khi nếu chúng ta thay thế nó :bằng không gian, thì việc chia tách chỉ được thực hiện foovà bar. Đó là hàng đầu và dấu vết bị bỏ qua, và chuỗi của chúng được coi như một. Có các quy tắc bổ sung khi các ký tự khoảng trắng và không khoảng trắng được kết hợp trong $IFS. Một số triển khai có thể thêm / xóa điều trị đặc biệt bằng cách nhân đôi các ký tự trong IFS ( IFS=::hoặc IFS=' ').
Vì vậy, ở đây, nếu chúng ta không muốn xóa các ký tự khoảng trắng không bị bỏ sót hàng đầu và dấu, chúng ta cần xóa các ký tự khoảng trắng IFS khỏi IFS.
Ngay cả với các ký tự không phải khoảng trắng IFS, nếu dòng đầu vào chứa một (và chỉ một) trong số các ký tự đó và đó là ký tự cuối cùng trong dòng (như IFS=: read -r wordtrên đầu vào như foo:) với các vỏ POSIX (không zshphải một số pdkshphiên bản), đầu vào đó được coi là một footừ vì trong các shell đó, các ký tự $IFSđược coi là dấu kết , vì vậy wordsẽ chứa foo, không foo:.
Vì vậy, cách chuẩn để đọc một dòng đầu vào với readnội dung là:
IFS= read -r line
(lưu ý rằng đối với hầu hết các readtriển khai, chỉ hoạt động đối với các dòng văn bản vì ký tự NUL không được hỗ trợ ngoại trừ trong zsh).
Sử dụng var=value cmdcú pháp đảm bảo IFSchỉ được đặt khác nhau trong khoảng thời gian của cmdlệnh đó .
Ghi chú lịch sử
Nội dung readđược giới thiệu bởi trình bao Bourne và đã đọc các từ , không phải các dòng. Có một vài khác biệt quan trọng với đạn POSIX hiện đại.
Các vỏ Bourne readkhông hỗ trợ một -rtùy chọn (được giới thiệu bởi vỏ Korn), vì vậy không có cách nào để vô hiệu hóa xử lý dấu gạch chéo ngoài việc xử lý trước đầu vào với một cái gì đó giống như sed 's/\\/&&/g'ở đó.
Shell Bourne không có khái niệm về hai lớp nhân vật (một lần nữa được giới thiệu bởi ksh). Trong trình bao Bourne, tất cả các ký tự trải qua xử lý giống như các ký tự khoảng trắng IFS thực hiện trong ksh, đó là IFS=: read a b ctrên một đầu vào như foo::barsẽ gán barcho $b, chứ không phải chuỗi rỗng.
Trong vỏ Bourne, với:
var=value cmd
Nếu cmdlà một tích hợp (như readlà), varvẫn được đặt thành valuesau khi cmdđã kết thúc. Điều đó đặc biệt quan trọng $IFSbởi vì trong vỏ Bourne, $IFSđược sử dụng để phân chia mọi thứ, không chỉ các bản mở rộng. Ngoài ra, nếu bạn loại bỏ ký tự khoảng $IFStrắng trong shell Bourne, sẽ "$@"không còn hoạt động.
Trong shell Bourne, việc chuyển hướng một lệnh ghép khiến nó chạy trong một lớp con (trong các phiên bản đầu tiên, ngay cả những thứ như read var < filehoặc exec 3< file; read var <&3không hoạt động), do đó, hiếm khi sử dụng shell Bourne readcho mọi thứ trừ đầu vào của người dùng trên thiết bị đầu cuối (trong đó xử lý tiếp tục dòng có ý nghĩa)
Một số Unice (như HP / UX, cũng có một trong util-linux) vẫn có linelệnh đọc một dòng đầu vào (trước đây là lệnh UNIX tiêu chuẩn cho đến phiên bản Đặc tả UNIX đơn 2 ).
Điều đó về cơ bản giống như head -n 1ngoại trừ việc nó đọc một byte mỗi lần để đảm bảo rằng nó không đọc nhiều hơn một dòng. Trên các hệ thống đó, bạn có thể làm:
line=`line`
Tất nhiên, điều đó có nghĩa là sinh ra một quy trình mới, thực hiện một lệnh và đọc đầu ra của nó thông qua một đường ống, do đó kém hiệu quả hơn nhiều so với ksh IFS= read -r line, nhưng vẫn trực quan hơn rất nhiều.