bash thay đổi hành vi của nó tùy thuộc vào giá trị của biến IFS '


18

Khi tôi đặt IFSbiến thành một khoảng bashtrắng , coi nhiều khoảng trắng là một khoảng trắng ( myprogramlà chương trình in các đối số dòng lệnh mà nó nhận được):

IFS=" "
x="hello   hi   world"
./myprogram $x
argv[1] = hello
argv[2] = hi
argv[3] = world

Nhưng khi tôi đặt IFSbiến thành dấu phẩy, bashkhông coi nhiều dấu phẩy là một dấu phẩy:

IFS=","
x="hello,,,hi,,,world"
./myprogram $x
argv[1] = hello
argv[2] = 
argv[3] = 
argv[4] = hi
argv[5] = 
argv[6] = 
argv[7] = world

Tại sao vậy?


Chỉ để tham khảo, "IFS" có nghĩa là Dấu tách trường nội bộ .
pr1268

Câu trả lời:


21

Điều này được ghi lại trong man bash. Một lần xuất hiện của bất kỳ ký tự nào trong IFS không phải là khoảng trắng phân định một trường.

Từ man bash:

Shell xử lý từng ký tự của IFS như một dấu phân cách và chia kết quả của các phần mở rộng khác thành các từ bằng cách sử dụng các ký tự này làm dấu kết thúc trường. Nếu IFS là unset, hoặc giá trị của nó là chính xác <space><tab><newline>, mặc định, sau đó trình tự <space>, <tab><newline>vào lúc bắt đầu và kết thúc của các kết quả của việc mở rộng trước đó được bỏ qua, và mọi chuỗi ký tự IFS không ở đầu hoặc cuối phục vụ để phân định từ ngữ. Nếu IFS có một giá trị khác với mặc định, thì các chuỗi của không gian ký tự khoảng trắng, tab và dòng mới sẽ bị bỏ qua ở đầu và cuối của từ, miễn là ký tự khoảng trắng nằm trong giá trị của IFS (ký tự khoảng trắng IFS ). Bất kỳ ký tự nào trong IFS không phải là khoảng trắng IFS, cùng với bất kỳ ký tự khoảng trắng IFS liền kề nào, sẽ phân định một trường. Một chuỗi các ký tự khoảng trắng IFS cũng được coi là một dấu phân cách. Nếu giá trị của IFS là null, không có sự phân tách từ nào xảy ra. [Nhấn mạnh thêm.]

Ví dụ: chia tách trường

Nếu IFS không có ký tự khoảng trắng, thì khoảng trắng được bao gồm trong các trường:

$ ( IFS=',' x='one , two,three'; printf "<%s>\n" $x )
<one >
< two>
<three>

Nếu IFS có cả khoảng trắng và dấu phẩy, thì các chuỗi khoảng trống, theo sau là dấu phẩy, theo sau là các khoảng trống được coi là một dấu phân cách duy nhất:

$ ( IFS=' ,' x='one , two,three'; printf "<%s>\n" $x )
<one>
<two>
<three>

Chuỗi dấu phẩy được hiểu là chuỗi các trường trống:

$ ( IFS=' ,' x='one,,,two,three'; printf "<%s>\n" $x )
<one>
<>
<>
<two>
<three>

Ví dụ: khoảng trắng hàng đầu và dấu

Nếu IFS không chứa khoảng trắng, thì mọi khoảng trắng ở đầu và cuối được giữ trong các trường:

$ ( IFS=',' x='  one , two,three  ,'; printf "<%s>\n" $x )
<  one >
< two>
<three  >

Nếu IFS không có khoảng trống, thì bất kỳ chuỗi khoảng trống hàng đầu hoặc dấu vết nào sẽ bị xóa:

$ ( IFS=' ,' x='  one , two,three  ,'; printf "<%s>\n" $x )
<one>
<two>
<three>

có lẽ cũng đáng nhấn mạnh "sau đó các chuỗi không gian ký tự khoảng trắng, tab và dòng mới bị bỏ qua ở đầu và cuối của từ, miễn là ký tự khoảng trắng nằm trong giá trị của IFS"
Jeff Schaller

@JeffSchaller Ý tưởng tuyệt vời: Tôi vừa thêm một phần về điều đó.
John1024


Điều gì nếu bạn có một tệp được phân tách bằng tab với một số giá trị bị thiếu? tức là bạn không muốn các chuỗi tab được coi là một tab duy nhất. Ngoài ra, các trường có dấu phẩy nên không thể sử dụng dấu phân cách. Là giải pháp duy nhất để sử dụng một số dấu phân cách khác (không phải tab)?
Davos

@Davos Đối với dữ liệu với mỗi trường được giới hạn bởi một tab, việc sử dụng các công cụ khác có thể xử lý việc này dễ dàng hơn như awkvới -F'\t'tùy chọn hoặc có thể tự nhiên hơn cut. Ngoài ra, nếu bạn có một phiên bản mới của bash, bạn có thể phân tích các lĩnh vực sử dụng readarrayvới các -d$'\t'tùy chọn.
John1024
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.