đọc -a mảng -d '\ n' <foo, mã thoát 1


8

Nếu tôi cố gắng thực hiện

read -a fooArr -d '\n' < bar

mã thoát là 1 - mặc dù nó hoàn thành những gì tôi muốn; đặt từng dòng bartrong một phần tử của mảng fooArr(sử dụng bash 4.2.37).

Ai đó có thể giải thích tại sao điều này đang xảy ra


Tôi đã tìm ra những cách khác để giải quyết vấn đề này, như những cách bên dưới, vì vậy đó không phải là điều tôi đang yêu cầu.

for ((i=1;; i++)); do
    read "fooArr$i" || break;
done < bar

hoặc là

mapfile -t fooArr < bar

Câu trả lời:


14

Điều cần được giải thích là lệnh xuất hiện để hoạt động, không phải mã thoát của nó

'\n'là hai ký tự: dấu gạch chéo ngược \và chữ cái n. Những gì bạn nghĩ rằng bạn cần $'\n'là một nguồn cấp dữ liệu (nhưng điều đó cũng không đúng, xem bên dưới).

Các -dtùy chọn thực hiện điều này:

  -d delim  continue until the first character of DELIM is read, rather
            than newline

Vì vậy, nếu không có tùy chọn đó, readsẽ đọc đến một dòng mới, chia dòng thành các từ bằng cách sử dụng các ký tự $IFSlàm dấu phân cách và đặt các từ vào mảng. Nếu bạn đã chỉ định -d $'\n', đặt dấu phân cách dòng thành một dòng mới, nó sẽ thực hiện chính xác điều tương tự . Cài đặt -d '\n'có nghĩa là nó sẽ đọc đến dấu gạch chéo ngược đầu tiên (nhưng, một lần nữa, xem bên dưới), là ký tự đầu tiên trong delim. Vì không có dấu gạch chéo ngược trong tệp của bạn, nên readsẽ chấm dứt ở cuối tệp và:

Exit Status:
The return code is zero, unless end-of-file is encountered, read times out,
or an invalid file descriptor is supplied as the argument to -u.

Vì vậy, đó là lý do tại sao mã thoát là 1.

Từ thực tế là bạn tin rằng lệnh đã hoạt động, chúng tôi có thể kết luận rằng không có khoảng trắng trong tệp, do đó read, sau khi đọc toàn bộ tệp với hy vọng vô ích tìm thấy dấu gạch chéo ngược, sẽ phân tách nó bằng khoảng trắng (giá trị mặc định là $IFS), bao gồm cả các dòng mới. Vì vậy, mỗi dòng (hoặc mỗi từ, nếu một dòng chứa nhiều hơn một từ) được đưa vào mảng.

Vụ án bí ẩn về dấu gạch chéo ngược

Bây giờ, làm thế nào tôi biết tập tin không chứa bất kỳ dấu gạch chéo ngược nào? Bởi vì bạn đã không cung cấp -rcờ cho read:

  -r                do not allow backslashes to escape any characters

Vì vậy, nếu bạn có bất kỳ dấu gạch chéo ngược nào trong tệp, chúng sẽ bị xóa, trừ khi bạn có hai trong số chúng liên tiếp. Và, tất nhiên, có bằng chứng readcó mã thoát là 1, chứng tỏ rằng nó không tìm thấy dấu gạch chéo ngược, vì vậy cũng không có hai trong số chúng liên tiếp.

Hành trình

Bash sẽ không bị bash nếu không có gotchas ẩn đằng sau mỗi lệnh, và readcũng không ngoại lệ. Đây là một cặp vợ chồng:

  1. Trừ khi bạn chỉ định -r, readsẽ diễn giải các chuỗi thoát dấu gạch chéo ngược. Trừ khi đó thực sự là những gì bạn muốn (đôi khi nó là, nhưng chỉ thỉnh thoảng), bạn nên nhớ chỉ định -rđể tránh các ký tự biến mất trong trường hợp hiếm hoi có dấu gạch chéo ngược trong đầu vào.

  2. Việc readtrả về mã thoát là 1 không có nghĩa là nó thất bại. Nó cũng có thể đã thành công, ngoại trừ việc tìm ra kẻ kết thúc dòng. Vì vậy, hãy cẩn thận với một vòng lặp như thế này: while read -r LINE; do something with LINE; done bởi vì nó sẽ thất bại do somethingvới dòng cuối cùng trong trường hợp hiếm hoi là dòng cuối cùng không có dòng mới ở cuối.

  3. read -r LINE bảo tồn dấu gạch chéo ngược, nhưng nó không giữ khoảng trắng hàng đầu hoặc dấu.


Cảm ơn! Tôi nghĩ rằng tôi đã thử cách tiếp cận $ '\ n', nhưng tôi đoán là không: / rất vui được biết về -r mặc dù
RasmusWL

2
"trường hợp hiếm hoi mà dòng cuối cùng không có dòng mới" dường như không phải là một lý do thuyết phục để khuyên "không bao giờ sử dụng while read". Nếu không, câu trả lời tuyệt vời.
glenn jackman

@glennjackman: Bạn có thể sử dụng while readmiễn là bạn không có bất cứ thứ gì trong vòng lặp. Mặt khác, đó là một lỗi đang chờ để cắn bạn - và tin tôi đi, tôi đã bị cắn.
rici

2
Dòng cuối cùng luôn có một dòng mới. Đó là cách một dòng được định nghĩa: nó kết thúc bằng một dòng mới. Nếu một tệp không trống không kết thúc bằng một dòng mới, thì đó không phải là tệp văn bản và bạn không thể sử dụng các công cụ xử lý văn bản như tiện ích shell read.
Gilles 'SO- ngừng trở nên xấu xa'

2
@glennjackman: Tôi, tôi lặp qua các tệp được phân loại bằng awk, chủ yếu. Để lặp lại toàn bộ dòng mà tệp không quá lớn, mapfilekhá tuyệt. Để hack nhanh và bẩn, tôi sẽ sử dụng vòng lặp while bị cấm, nhưng tôi đã ngừng đưa nó vào tập lệnh sản xuất. YMMV và tôi làm dịu cảnh báo trong câu trả lời.
rici

4

Đó là hành vi dự kiến:

Mã trả về bằng 0, trừ khi gặp phải tập tin cuối, [...]

start cmd:> echo a b c | { read -a testarray; echo $?; }
0

start cmd:> echo -n a b c | { read -a testarray; echo $?; }
1
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.