Q1. Trường tách.
Là trường tách giống như chia từ?
Vâng, cả hai đều chỉ đến cùng một ý tưởng.
Câu 2: Khi nào IFS null ?
Có phải thiết lập IFS=''
giống như null, cũng giống như một chuỗi rỗng?
Có, cả ba đều có nghĩa giống nhau: Không được tách trường / từ. Ngoài ra, điều này ảnh hưởng đến các trường in (như với echo "$*"
) tất cả các trường sẽ được nối với nhau không có khoảng trống.
Câu 3: (phần a) Bỏ đặt IFS.
Trong đặc tả POSIX, tôi đọc như sau :
Nếu IFS không được đặt, shell sẽ hoạt động như thể giá trị của IFS là <dấu cách> <tab> <newline> .
Điều này hoàn toàn tương đương với:
Với một unset IFS
, shell sẽ hoạt động như thể IFS là mặc định.
Điều đó có nghĩa là 'Chia tách trường' sẽ hoàn toàn giống với giá trị IFS mặc định hoặc không được đặt.
Điều đó KHÔNG có nghĩa là IFS sẽ hoạt động theo cùng một cách trong mọi điều kiện. Cụ thể hơn, việc thực thi OldIFS=$IFS
sẽ đặt var OldIFS
thành null , không phải mặc định. Và cố gắng đặt lại IFS, vì điều này, IFS=OldIFS
sẽ đặt IFS thành null, không giữ cho nó không được đặt như trước đây. Cẩn thận !!.
Câu 3: (phần b) Khôi phục IFS.
Làm thế nào tôi có thể khôi phục giá trị của IFS về mặc định. Nói rằng tôi muốn khôi phục giá trị mặc định của IFS. Làm thế nào để làm điều đó? (cụ thể hơn, làm cách nào để tôi tham khảo <tab> và <newline> ?)
Đối với zsh, ksh và bash (AFAIK), IFS có thể được đặt thành giá trị mặc định là:
IFS=$' \t\n' # works with zsh, ksh, bash.
Xong, bạn không cần đọc gì nữa.
Nhưng nếu bạn cần thiết lập lại IFS cho sh, nó có thể trở nên phức tạp.
Chúng ta hãy xem từ dễ nhất để hoàn thành mà không có nhược điểm (ngoại trừ độ phức tạp).
1.- Bỏ đặt IFS.
Chúng ta chỉ có thể unset IFS
(Đọc phần 3 phần a, ở trên.).
2.- Hoán đổi ký tự.
Như một giải pháp thay thế, việc hoán đổi giá trị của tab và dòng mới giúp việc đặt giá trị của IFS đơn giản hơn và sau đó nó hoạt động theo cách tương đương.
Đặt IFS thành <dấu cách> <dòng mới> <tab> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Đơn giản? giải pháp:
Nếu có các tập lệnh con cần IFS được đặt chính xác, bạn luôn có thể viết thủ công:
IFS = '
'
Trường hợp trình tự được nhập thủ công là : IFS=
'spacetabnewline', trình tự đã được nhập chính xác ở trên (Nếu bạn cần xác nhận, hãy chỉnh sửa câu trả lời này). Nhưng một bản sao / dán từ trình duyệt của bạn sẽ bị hỏng vì trình duyệt sẽ ép / ẩn khoảng trắng. Nó gây khó khăn cho việc chia sẻ mã như được viết ở trên.
4.- Giải pháp hoàn chỉnh.
Để viết mã có thể được sao chép một cách an toàn thường liên quan đến các lối thoát có thể in rõ ràng.
Chúng ta cần một số mã "tạo ra" giá trị mong đợi. Nhưng, ngay cả khi đúng về mặt khái niệm, mã này sẽ KHÔNG đặt dấu vết \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Điều đó xảy ra bởi vì, trong hầu hết các hệ vỏ, tất cả các dòng mới thay thế $(...)
hoặc `...`
thay thế lệnh được loại bỏ khi mở rộng.
Chúng ta cần sử dụng một mẹo cho sh:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Một cách khác có thể là đặt IFS làm giá trị môi trường từ bash (ví dụ) và sau đó gọi sh (các phiên bản của nó chấp nhận IFS được đặt qua môi trường), như sau:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Nói tóm lại, sh làm cho việc đặt lại IFS để mặc định là một cuộc phiêu lưu kỳ quặc.
Q4: Trong mã thực tế:
Cuối cùng, làm thế nào mã này:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
cư xử nếu chúng ta thay đổi dòng đầu tiên thành
while read -r line # Use the default IFS value
hoặc để:
while IFS=' ' read -r line
Đầu tiên: Tôi không biết liệu echo $line
(với var KHÔNG được trích dẫn) có ở trên porpouse hay không. Nó giới thiệu một mức độ thứ hai của 'chia tách trường' mà đọc không có. Vì vậy, tôi sẽ trả lời cả hai. :)
Với mã này (để bạn có thể xác nhận). Bạn sẽ cần xxd hữu ích :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Tôi có:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
Giá trị đầu tiên chỉ là giá trị chính xác của IFS=
'spacetabnewline'
Dòng tiếp theo là tất cả các giá trị hex mà var $a
có và dòng mới '0a' ở cuối vì nó sẽ được trao cho mỗi lệnh đọc.
Dòng tiếp theo, mà IFS là null, không thực hiện bất kỳ 'chia tách trường' nào, nhưng dòng mới bị xóa (như mong đợi).
Ba dòng tiếp theo, vì IFS chứa một khoảng trắng, xóa các khoảng trắng ban đầu và đặt dòng var thành số dư còn lại.
Bốn dòng cuối cùng cho thấy một biến không được trích dẫn sẽ làm gì. Các giá trị sẽ được phân chia trên (một số) khoảng trắng và sẽ được in dưới dạng:bar,baz,qux,
IFS
và không đặtIFS
là rất khác nhau. Câu trả lời cho Q4 là một phần sai: các dải phân cách bên trong không được chạm vào đây, chỉ có các dải dẫn và dấu.