Trong `while IFS = read..`, tại sao IFS không có hiệu lực?


12

Tôi có thể có một cái gì đó hoàn toàn sai, nhưng nó có vẻ thuyết phục đối với tôi, rằng đặt IFS là một trong những lệnh trong danh sách trước khi làm / hoàn thành không có tác dụng.
IFS bên ngoài (bên ngoài whilecấu trúc) chiếm ưu thế trong tất cả các ví dụ được hiển thị trong tập lệnh bên dưới ..

Những gì đang xảy ra ở đây? Tôi có hiểu sai về những gì IFS làm trong tình huống này không? Tôi dự kiến ​​các kết quả phân chia mảng sẽ được hiển thị trong cột "dự kiến".


#!/bin/bash
xifs() { echo -n "$(echo -n "$IFS" | xxd -p)"; } # allow for null $IFS 
show() { x=($1) 
         echo -ne "  (${#x[@]})\t |"
         for ((j=0;j<${#x[@]};j++)); do 
           echo -n "${x[j]}|"
         done
         echo -ne "\t"
         xifs "$IFS"; echo
}
data="a  b   c"
echo -e "-----   --  -- \t --------\tactual"
echo -e "outside        \t  IFS    \tinside" 
echo -e "loop           \t Field   \tloop" 
echo -e "IFS     NR  NF \t Split   \tIFS (actual)" 
echo -e "-----   --  -- \t --------\t-----"
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 1'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 2'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 3'; show "$REPLY"; done
IFS=" ";      xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 4'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 5'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 6'; show "$REPLY"; done
IFS=;         xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 7'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t 8'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 9'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t10'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t11'; show "$REPLY"; done
echo -e "-----   --  -- \t --------\t-----"

Đầu ra:

-----   --  --   --------       actual   
outside           IFS           inside                assigned   
loop             Field          loop    #              inner
IFS     NR  NF   Split          IFS     #  expected    IFS
-----   --  --   --------       -----   #  ---------  --------
20090a   1  (3)  |a|b|c|        20090a  #                              
20090a   2  (3)  |a|b|c|        20090a  #  |a  b   c|  IFS=
20090a   3  (3)  |a|b|c|        20090a  #  |a  |   c|  IFS=b
20       4  (3)  |a|b|c|        20      #                          
20       5  (3)  |a|b|c|        20      #  |a  b   c   IFS=
20       6  (3)  |a|b|c|        20      #  |a  |   c|  IFS=b
         7  (1)  |a  b   c|             #                          
         8  (1)  |a  b   c|             #  |a|b|c|     IFS=" "
         9  (1)  |a  b   c|             #  |a  |   c|  IFS=b
62      10  (2)  |a  |   c|     62      #  |a  b   c|  IFS=
62      11  (2)  |a  |   c|     62      #  |a|b|c|     IFS=" "
-----   --  --   --------       -----      ---------   -------                        

Câu trả lời:


17

(Xin lỗi, giải thích dài)

Có, IFSbiến trong while IFS=" " read; do …không có ảnh hưởng đến phần còn lại của mã.

Trước tiên, hãy chính xác rằng dòng lệnh shell có hai loại biến khác nhau:

  • biến shell (chỉ tồn tại trong shell và là cục bộ của shell)
  • biến môi trường, tồn tại cho mọi quá trình. Chúng thường được bảo tồn theo fork()exec()vì vậy các quá trình con kế thừa chúng.

Khi bạn gọi một lệnh với:

  A=foo B=bar command

lệnh được thực thi trong một môi trường nơi biến (môi trường) Ađược đặt thành fooBđược đặt thành bar. Nhưng với dòng lệnh này, các biến shell hiện tại ABđược giữ nguyên .

Điều này khác với:

A=foo; B=bar; command

Ở đây, các biến shell ABđược định nghĩa và lệnh được chạy mà không có biến môi trường ABđược xác định. Giá trị ABkhông thể truy cập từ command.

Tuy nhiên, nếu một số biến shell được export-ed, các biến môi trường tương ứng được đồng bộ hóa với các biến shell tương ứng của chúng. Thí dụ:

export A
export B
A=foo; B=bar; command

Với mã này, cả biến shell và biến môi trường shell được đặt thành foobar. Vì các biến môi trường được kế thừa bởi các quy trình phụ, nên commandsẽ có thể truy cập các giá trị của chúng.

Để quay lại câu hỏi ban đầu của bạn, trong:

IFS='a' read

chỉ readbị ảnh hưởng. Và trên thực tế, trong trường hợp này, readkhông quan tâm đến giá trị của IFSbiến. Nó IFSchỉ sử dụng khi bạn yêu cầu chia dòng (và được lưu trữ trong một số biến), như trong:

echo "a :  b :    c" | IFS=":" read i j k; \
    printf "i is '%s', j is '%s', k is '%s'" "$i" "$j" "$k"

IFSkhông được sử dụng bởi readtrừ khi nó được gọi với các đối số. ( Chỉnh sửa: Điều này không chính xác: các ký tự khoảng trắng, tức là khoảng trắng và tab, hiện diện IFSluôn bị bỏ qua ở đầu / cuối dòng đầu vào.)


Thật là một lời giải thích tuyệt vời! Nó thật đơn giản! Tôi đã bị bối rối bởi cú pháp 'không có dấu chấm phẩy' trong nhiều tháng; và nó chỉ đơn giản là một trường hợp của nó có nghĩa là một biến cục bộ! .. rozcietrzewiacz đã mở đường cho tôi ( câu hỏi lớn) trong câu hỏi khác ... và bạn vừa đặt kem vào bánh ... Tôi đã suốt đêm về điều này, và nó chắc chắn có giá trị cho câu trả lời tốt và rõ ràng như vậy! .. Cảm ơn bạn ..
Peter.O

Ừm. Tôi đã phải đọc nhận xét chỉnh sửa đó nhiều lần trước khi tôi nhận được nó - ý bạn là nói rằng các ký tự khoảng trắng có trong $IFSđó bị xóa ở đầu / cuối dòng đầu vào, tôi đoán vậy? (Đó là cách nó hoạt động.)
zrajm


Giá trị của IFS rất quan trọng ngay cả khi đọc một biến duy nhất, vì shell vẫn thực hiện phân tách từ trên đầu vào. Vì vậy, ví dụ, nhập các ký tự a<tab>bvào read varsẽ dẫn đến var có giá trị a<space>b, nhưng nếu thay vào đó bạn có IFS='<newline>' read varthì giá trị của var sẽ là a<tab>b.
John Hascall

8

Nói một cách đơn giản, bạn phải đọc nhiều hơn một biến tại một thời điểm để IFS=<something> read ...cấu trúc có hiệu ứng rõ ràng trong các ví dụ 1 của bạn .

Bạn bỏ lỡ phạm vi read trong các ví dụ. Không sửa đổi IFS bên trong vòng lặp trong các trường hợp thử nghiệm của bạn. Cho phép tôi chỉ ra chính xác, nơi IFS thứ hai có tác dụng trong mỗi dòng của bạn:

 IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo ...
                                                      ^      ^
                                                      |      |
                                          from here --'       `- to here :)

Nó giống như với bất kỳ chương trình được thực hiện trong shell. Biến bạn (tái) xác định tại dòng lệnh ảnh hưởng đến việc thực hiện chương trình. Và chỉ có thế (vì bạn không xuất). Do đó, để sử dụng định nghĩa lại IFStrong dòng như vậy, bạn phải yêu cầu readgán giá trị cho nhiều hơn một biến . Hãy xem những ví dụ sau:

 $ data="a  b   c"
 $ echo "$data" | while           read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a|b||c|
 $ echo "$data" | while IFS=      read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a b c||||
 $ echo "$data" | while IFS='a'   read A B C; do echo \|$A\|$B\|\|$C\|; done
 || b c|||
 $ echo "$data" | while IFS='ab'  read A B C; do echo \|$A\|$B\|\|$C\|; done
 || || c|

1 Như tôi vừa học được từ Gilles , thực sự có thể có một lợi ích của việc thiết lập IFS=''(để trống) khi chỉ đọc một trường: nó tránh cắt ngắn khoảng trắng ở đầu dòng.


Tốt .. Cảm ơn ... Tôi đã nhận được nó lần này .. và tôi thích bản phác thảo của bạn :)
Peter.O

OK, bây giờ tôi đã đọc bình luận của bạn và thấy bạn chưa nhận thấy câu trả lời của tôi cho vấn đề đó trong câu hỏi khác. Có lẽ bạn chỉ có thể hoàn nguyên cái khác và xóa cái này, vì nó thực sự là một vấn đề chung?
rozcietrzewiacz

Đúng, hai câu hỏi có một chủ đề liên quan, nhưng tiêu đề của câu hỏi còn lại là "Tại sao được IFS= readsử dụng theo sở thích để chỉ thiết lập lại biến môi trường IFS". Sau đó, tôi không có nhận thức rằng các biến cục bộ có thể được đặt bởi người gọi lệnh. Đó là câu trả lời cho câu hỏi đó. Nó đã phát triển hơn nữa để giải quyết vấn đề chính của câu hỏi này, nhưng đến khi tôi nhận ra điều đó, tôi đã hỏi câu hỏi này ... Có lẽ hai câu hỏi tương tự như hai sedcâu hỏi, vì vậy tôi cảm thấy nó giữ được ... Thêm tiêu đề cho nhân viên Google.
Peter.O
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.