( jw013 đã đưa ra một câu trả lời đúng , nhưng tôi muốn chỉ ra một vài vấn đề nữa.)
Phía bên phải của một đường ống chạy trong lớp con riêng của nó trong bash (cũng như hầu hết các shell khác, ngoại lệ là ATT ksh và zsh). Vì vậy, bạn đang đặt các biến môi trường trong lớp con thực thi vòng lặp, nhưng không phải trong quy trình trình bao chính.
Đây là một cách khắc phục dễ dàng để thay thế việc sử dụng vô dụng cat
bằng cách chuyển hướng:
while read …; do …; done <"$1"
Nói chung, nếu bạn cần xử lý trước đầu vào, hãy đặt vòng lặp và phần còn lại của tập lệnh (ít nhất là phần cần các biến đã thay đổi) bên trong một khối.
grep '^foo_' "$1" | {
while read …; do …; done
do_something
}
Lưu ý rằng nói chung, while read line; do …
không hoàn toàn lặp lại các dòng đầu vào. Xem tại sao được while IFS= read
sử dụng thường xuyên, thay vì IFS=; while read..
? cho một lời giải thích. Thành ngữ chính xác để lặp qua các dòng đầu vào là
while IFS= read -r line; do …
Ở đây, việc loại bỏ khoảng trắng hàng đầu và dấu vết không thành vấn đề vì không nên có bất kỳ đầu vào mẫu nào của bạn, nhưng bạn có thể cần -r
tránh mở rộng dấu gạch chéo ngược. while read line
Thực tế có thể đó là một cách chính xác để đọc đầu vào của bạn (nhưng tôi chỉ nghi ngờ nếu các giá trị biến không chứa dòng mới). Cũng có thể định dạng đầu vào của bạn không rõ ràng hoặc phức tạp hơn để phân tích cú pháp. Kiểm tra xem điều gì xảy ra nếu một trong các giá trị của biến chứa ký tự dòng mới, trích dẫn kép hoặc dấu gạch chéo ngược.
Một điểm mà bạn chắc chắn đang xáo trộn đầu vào là khi bạn trích xuất giá trị:
val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
Trước tiên, bạn cần sử dụng dấu ngoặc kép xung quanh thay thế biến : echo "${kv#*=}"
; mặt khác, shell thực hiện phân tách từ và tạo tên tệp (nghĩa là tạo khối) trên nó. Thứ hai, echo
không phải là một cách đáng tin cậy để in một chuỗi, bởi vì một số chuỗi bắt đầu bằng một -
được coi là tùy chọn và một số shell thực hiện mở rộng dấu gạch chéo ngược trên đối số. Một cách đáng tin cậy và di động để in một chuỗi là printf %s "${kv#*=}"
. Ngoài ra, nếu giá trị kéo dài nhiều dòng, chương trình sed sẽ hoạt động trên từng dòng riêng biệt, điều này là sai. Điều này có thể sửa được, nhưng có một phương pháp dễ dàng hơn, đó là sử dụng các phương tiện thao tác chuỗi của shell : val=${kv#*=}; val=${val#\"}; val=${val%\"}
.
Giả sử rằng đầu vào của bạn được phân tích cú pháp chính xác bằng một đơn giản read
, đây là cách đơn giản hơn để phân tích cú pháp, tận dụng IFS
cài đặt để phân tách từng dòng:
while read name value; do
value=${value#\"}; value=${value%\"}
export name="$value"
done <"$1"
key=${kv%%=*}
tốt hơnkey=${kv%=*}
vì %% và ## khác với% và # cho dù phần bị xóa là phần có thể tháo rời ngắn nhất hoặc dài nhất