2015-04-17 12 views
18

Tôi có tập tin a.txt với nội dung sau đâyBash đọc dòng không đọc không gian hàng đầu

aaa 
    bbb 

Khi tôi thực hiện sau đây kịch bản:

while read line 
do 
    echo $line 
done <a.txt> b.txt 

tạo b.txt chứa sau

aaa 
bbb 

Nó được thấy rằng các không gian hàng đầu của các dòng đã bị xóa. Làm thế nào tôi có thể bảo tồn không gian hàng đầu?

Trả lời

27

này được bao phủ trong Bash mục Hỏi đáp về reading data line-by-line.

Lệnh đọc sửa đổi từng dòng đọc; theo mặc định, nó loại bỏ tất cả các ký tự khoảng trắng đầu và cuối (dấu cách và tab hoặc bất kỳ ký tự khoảng trống nào có trong IFS). Nếu đó là không mong muốn, biến IFS phải được xóa:

# Exact lines, no trimming 
while IFS= read -r line; do 
    printf '%s\n' "$line" 
done < "$file" 

Như Charles Duffy một cách chính xác chỉ ra (và tôi muốn bỏ bằng cách tập trung vào các vấn đề IFS); nếu bạn muốn xem các khoảng trống trong đầu ra của bạn, bạn cũng cần báo giá biến khi bạn sử dụng nó hoặc trình bao sẽ, một lần nữa, thả khoảng trắng.

Lưu ý về một số khác biệt khác trong đoạn trích được trích dẫn so với mã ban đầu của bạn.

Việc sử dụng đối số -r cho read được bao gồm trong một câu duy nhất ở đầu trang được liên kết trước đó.

Tùy chọn -r để đọc ngăn chặn việc giải thích dấu gạch chéo ngược (thường được sử dụng như cặp đường chéo ngược dòng mới, để tiếp tục qua nhiều dòng). Nếu không có tùy chọn này, mọi dấu gạch chéo ngược trong đầu vào sẽ bị hủy. Bạn hầu như luôn luôn nên sử dụng tùy chọn -r với đọc.

Như việc sử dụng printf thay vì echo có hành vi của echo là, một chút không may, không portably nhất quán trên tất cả các môi trường và sự khác biệt có thể khó khăn khi phải đối phó với. printf mặt khác là phù hợp và có thể được sử dụng hoàn toàn mạnh mẽ.

+5

Nếu bạn không cho 'đọc' bất kỳ đối số nào để sử dụng để giữ đầu vào (dựa vào biến mặc định' REPLY'), không có khoảng trống nào bị tước và bạn có thể bỏ qua sửa đổi thành 'IFS'. Tức là, 'trong khi đọc -r; làm printf '% s \ n' "$ REPLY"; đã thực hiện <"$ file" ' – chepner

+1

@chepner Thú vị. Tôi tự hỏi tại sao vậy. –

+2

Tôi không chắc chắn; nó dường như không được ghi chép như xa như tôi có thể nói. Nó có ý nghĩa nếu bạn nghĩ về nó như là không đối số yêu cầu chia dòng thành 0 lĩnh vực, có nghĩa là không có sử dụng cho 'IFS'. (Điều đó giả sử bạn chấp nhận rằng việc chia tách một dòng thành một trường vẫn là một sự chia tách, mặc dù là một sự thoái hóa.) Trong mọi trường hợp, nó là một 'bash'ism; POSIX 'read' yêu cầu ít nhất một đối số. – chepner

9

Có một số vấn đề ở đây:

  • Trừ IFS sẽ bị xóa, read dải hàng đầu và dấu khoảng trắng.
  • echo $line chia tách chuỗi và mở rộng nội dung của $line, chia nhỏ nội dung thành các từ riêng lẻ và chuyển những từ đó dưới dạng đối số riêng lẻ đến lệnh echo. Do đó, ngay cả khi IFS được xóa tại thời điểm read, echo $line vẫn sẽ loại bỏ khoảng trắng đầu và cuối và thay đổi các khoảng trắng giữa các từ thành một ký tự khoảng trắng. Ngoài ra, một dòng chỉ chứa ký tự * sẽ được mở rộng để chứa danh sách tên tệp.
  • echo "$line" là một cải tiến đáng kể, nhưng vẫn không xử lý chính xác các giá trị như -n, mà nó xử lý như chính đối số echo. printf '%s\n' "$line" sẽ sửa lỗi này hoàn toàn.
  • read mà không cần -r xử lý dấu gạch chéo ngược làm ký tự tiếp tục thay vì nội dung theo nghĩa đen, sao cho chúng không được bao gồm trong các giá trị được tạo trừ khi được tăng gấp đôi để tự thoát.

Như vậy:

while IFS= read -r line; do 
    printf '%s\n' "$line" 
done 
+0

Lời khuyên tốt, nhưng chuỗi hai ký tự '\ n' không _not_ dẫn đến _newline_, kết quả là _literal' n'_. Ngược lại, một '\' -escaped _actual_ newline làm 'read' để đọc dòng _following_, và để trực tiếp nối nó vào dòng hiện tại (loại bỏ' \ 'và dòng mới). Một '\' trước khi bất kỳ ký tự nào khác chỉ đơn giản là bị loại bỏ. – mklement0

+2

Một cách khác để mô tả hành vi của 'đọc' mà không có' -r': đầu vào được phân tích cú pháp giống như một thanh ghi có các ký tự '\' -cấu hình riêng lẻ được phân tích bởi chính (POSIX) vỏ (ví dụ, như một phần của một danh sách đối số), như được mô tả tại http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01 và về cơ bản được sao chép trong thông số POSIX 'read' tại http: //pubs.opengroup. org/onlinepubs/9699919799/tiện ích/read.html. – mklement0

+2

Cảm ơn bạn - Tôi sẽ muốn xem lại tài liệu nguồn để xác định cách tốt nhất để sửa đổi phần đó trong câu trả lời của tôi. –

Các vấn đề liên quan