Ý nghĩa của IFS = $ '\ n' trong kịch bản bash là gì?


163

Ở phần đầu của tập lệnh bash shell là dòng sau:

IFS=$'\n'

Ý nghĩa đằng sau bộ sưu tập các biểu tượng này là gì?


3
Xem thêm unix.stackexchange.com/questions/26784/under Hiểu-ifs và các câu hỏi mà nó trích dẫn.
Gilles

cú pháp vim nhấn mạnh đây là một lỗi cho tôi; sửa chữa?
theonlygusti

IFS=$'\n'là một bashism (+ shell khác, sử dụng Báo giá ANSI-C , để giải quyết, hãy xem stackoverflow.com/questions/10748703/ trên
pevik

Câu trả lời:


199

IFSlà viết tắt của "phân cách trường nội bộ". Nó được sử dụng bởi shell để xác định cách thực hiện phân tách từ, tức là cách nhận biết ranh giới từ.

Hãy thử điều này trong một shell như bash (các shell khác có thể xử lý việc này khác nhau, ví dụ zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

Giá trị mặc định cho IFSbao gồm các ký tự khoảng trắng (chính xác là: khoảng trắng, tab và dòng mới). Mỗi nhân vật có thể là một ranh giới từ. Vì vậy, với giá trị mặc định là IFS, vòng lặp ở trên sẽ in:

Word: foo:bar
Word: baz
Word: rab

Nói cách khác, shell nghĩ rằng khoảng trắng là một ranh giới từ.

Bây giờ, hãy thử thiết lập IFS=:trước khi thực hiện vòng lặp. Lần này, kết quả là:

Word: foo
Word: bar baz rab

Bây giờ, vỏ cũng phân tách mystringthành các từ - nhưng bây giờ, nó chỉ coi một dấu hai chấm là ranh giới từ.

Ký tự đầu tiên IFSlà đặc biệt: Nó được sử dụng để phân định các từ trong đầu ra khi sử dụng $*biến đặc biệt (ví dụ được lấy từ Hướng dẫn tập lệnh Bash nâng cao , nơi bạn cũng có thể tìm thêm thông tin về các biến đặc biệt như biến đó):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

So với:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

Lưu ý rằng trong cả hai ví dụ, vỏ vẫn sẽ đối xử với tất cả các nhân vật :, -;như ranh giới từ. Điều duy nhất thay đổi là hành vi của $*.

Một điều quan trọng khác cần biết là cái gọi là "khoảng trắng IFS" được xử lý như thế nào . Về cơ bản, ngay khi IFSbao gồm các ký tự khoảng trắng, khoảng trắng đầu và cuối được tách khỏi chuỗi được phân tách trước khi xử lý nó và một chuỗi các ký tự khoảng trắng liên tiếp cũng phân định các trường. Tuy nhiên, điều này chỉ áp dụng cho các ký tự khoảng trắng thực sự có trong đó IFS.

Ví dụ: chúng ta hãy nhìn vào chuỗi "a:b:: c d "(dấu cách khoảng trắng và hai ký tự khoảng trắng nằm giữa cd).

  1. Với IFS=:nó sẽ được chia thành bốn lĩnh vực: "a", "b", ""(trống string) và " c d "(một lần nữa, hai khoảng trống giữa cd). Lưu ý khoảng trắng hàng đầu và dấu trong trường cuối cùng.
  2. Với IFS=' :', nó sẽ được chia thành năm lĩnh vực: "a", "b", ""(trống string), "c""d". Không có khoảng trắng hàng đầu và dấu ở bất cứ đâu.

Lưu ý cách nhiều ký tự khoảng trắng liên tiếp phân định hai trường trong ví dụ thứ hai, trong khi nhiều ký tự liên tiếp không có (vì chúng không phải là ký tự khoảng trắng).

Đối với IFS=$'\n', đó là một ksh93cú pháp cũng được hỗ trợ bởi bash, zsh, mkshvà FreeBSD sh(với các biến thể giữa tất cả vỏ). Trích dẫn trang bash:

Các từ có dạng $ 'chuỗi' được xử lý đặc biệt. Từ này mở rộng thành "chuỗi", với các ký tự thoát dấu gạch chéo ngược được thay thế theo quy định của tiêu chuẩn ANSI C.

\nlà chuỗi thoát cho một dòng mới, vì vậy IFScuối cùng được đặt thành một ký tự dòng mới.


3
Điều này là tốt, nhưng, theo tôi, bạn sẽ làm tốt hơn rất nhiều để đọc và hiểu thông số POSIX hơn là bashhướng dẫn về kịch bản, hoặc bất cứ điều gì. Trong chính, thông tin có sẵn tại các liên kết như thế là thiếu theo những cách quan trọng. Dù sao, do đó bỏ lỡ hai điểm quan trọng liên quan đến việc tách vỏ - khoảng trắng toàn cầu và IFS.
mikeerv 14/2/2015

@mikeerv Cảm ơn, tôi đã thêm một số thông tin về khoảng trắng IFS. Không biết về điều đó. :)
Tblue 14/2/2015

4
Không liên quan, nhưng nếu bạn tò mò, bạn có thể muốn xem cách unset IFSlàm cho một chiếc vỏ hoạt động rất khác so với IFS=. Ngoài ra, byte đầu tiên trong IFS cũng đặc biệt "${named_array[*]}"- nhưng không có vấn đề gì khi việc mở rộng không được trích dẫn ..
mikeerv

Một vài điểm nữa: chia tách 1- từ, được điều chỉnh bởi $IFSmột trong hai điều chính được thực hiện khi mở rộng một biến không được trích dẫn trong ngữ cảnh danh sách (đó là splitmột phần của split+globtoán tử). Một cái khác là Globing. Khi sử dụng phân tách công việc, bạn thường cần phải phát hành set -fđể vô hiệu hóa globphần.
Stéphane Chazelas

3
3- $IFScũng được sử dụng bởi readlệnh dựng sẵn
Stéphane Chazelas

22

Bên trong trích dẫn đơn búp bê, một số nhân vật được đánh giá đặc biệt. Ví dụ, \nđược dịch sang dòng mới.

Vì vậy, dòng đặc biệt này gán dòng mới cho IFS biến. Ngược lại, IFS là một biến đặc biệt trong bash: Dấu tách trường bên trong. Như đã man bashnói, nó

được sử dụng để phân tách từ sau khi mở rộng và để phân chia các dòng thành các từ bằng readlệnh dựng sẵn. Giá trị mặc định là <space><tab><newline>.


5
+1 để đề cập dollared single quoteskhác với trích dẫn đơn giản.
Snowcrash

2
@Snowcrash +1 khi nói +1 khi đề cập đến các trích dẫn đơn có búp bê khác với các trích dẫn đơn giản . Xin lỗi, không thể giúp nó :) Nhưng thực sự đó là một điều thực sự tốt để chỉ ra bởi vì nó quan trọng!
Pryftan

1
@Pryftan +1 cho +1 cho +1 ... bạn biết đấy ... nó thực sự quan trọng.
0xc0de

@ 0xc0de Chắc chắn đồng ý! Cảm ơn vì điều đó! :)
Pryftan

15

Nói ngắn gọn, IFS=$'\n'gán dòng mới \ncho biến IFS.

$'string'cấu trúc là một cơ chế trích dẫn sử dụng để giải mã ANSI C giống như các chuỗi thoát. Cú pháp này xuất phát từ ksh93, và là di động để vỏ hiện đại như bash, zsh, pdksh, busybox sh.

Cú pháp này không được xác định bởi POSIX, nhưng đã được chấp nhận cho vấn đề SUS 7 .


-1

Tôi thích giải thích $IFSqua ví dụ:
Suppsoe bạn muốn cp hoặc mv hoặc xử lý tệp khác, IFS trống rỗng bởi defualt, Khi các tệp của bạn có meta char hoặc khoảng trắng như:
Linux Administration.pdfhoặc Free Software Fundation.ogg, Chắc chắn bạn sẽ có probelm, Bởi vì: Linux xem xét một tách biệt param và Ad quản lý xem xét một param riêng biệt. Vì vậy, bash có built-in variable, Sau đó, bạn có thể khởi tạo IFS==$(echo -en "\n\b"), Sau đó bash loại bỏ bất kỳ meta char và khoảng trắng giữa tên tệp, Ví dụ:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS
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.