2017-01-23 27 views
8

我想知道IFS如何影響bash中的分詞。行爲是依賴於上下文的,這種方式看起來不符合分詞的直覺。如何在bash中控制IFS分詞

一般想法似乎很簡單。從bash手冊頁引用:

shell將IFS的每個字符視爲分隔符,並將其他擴展的結果拆分爲這些字符上的單詞。 ...請注意,如果不發生擴展,則不會執行分割。

這可以通過將IFS變量設置爲','並使用逗號分隔參數列表調用shell函數來輕鬆驗證。

echo_n() { 
    echo Num args: $#, Args: "[email protected]" 
} 
(IFS=',' 
    args=foo,bar,baz 
    echo_n $args 
) 

正如預期的那樣,這導致在三個不同的參數echo_n

Num args: 3, Args: foo bar baz 

調用echo_n直接用逗號分隔的列表,因爲沒有擴展觸發失敗。

IFS=, echo_n foo,bar,baz 

結果

Num args: 1, Args: foo,bar,baz 

到這裏事情似乎相當扭曲的,但我可以換我的頭周圍。當我們開始爲圖片添加循環時,事情會變得更加複雜。

(IFS=,; for i in foo,bar,baz ; do echo_n $i; done) 

導致

Num args: 3, Args: foo bar baz 

這違背的目的for循環。

現在,我可以通過強制某種形式的擴展觸發的任何幾個bash技巧強制IFS詞分裂。例如:

(IFS=,; for i in ${NO_VAR:-foo,bar,baz} ; do echo_n $i; done) 

導致

Num args: 1, Args: foo 
Num args: 1, Args: bar 
Num args: 1, Args: baz 

(訣竅在於具有缺省值評估未定義的變量NO_VAR。)

另一個類似的特技,依靠命令替換:

(IFS=,; for i in $(echo foo,bar,baz) ; do echo_n $i; done) 

所以,這裏的問題是:什麼是推薦的,慣用的方式t o是否控制執行IFS分詞的上下文?

回答

5

它實現爲什麼下失敗是很重要的:

$ IFS=, echo_n foo,bar,baz 
Num args: 1, Args: foo,bar,baz 

命令預分配IFS只適用echo_n; foo,bar,baz不會在,上分割,因爲在該命令行上發生任何分詞(或缺少該分詞)之前echo_n運行。

(IFS=,; for i in foo,bar,baz ; do echo_n $i; done) 

導致單次迭代,因爲IFS僅用於拆分膨脹的結果(和由read,見下文),而不是文字串。 shell在第一次解析命令行時完成的單詞拆分實際上是硬編碼的,只能在空白處分割。


這不是完全清楚你要完成什麼,但一個好的經驗法則是,如果你在全球範圍內設置的IFS值,你做錯了什麼(或者至少次優)。只有兩種情況下我記得有用地改性IFS

  1. IFS=, read -r a b c分裂包含逗號成多個(在此爲3)片的線。 IFS的更改在read的本地;它讀取的任何字符串都是完整讀取的,並且僅在內部分割read分割。

  2. foo=$(IFS=.; echo "${foo[*]}")將數組元素加入單個字符串並以.作爲分隔符。請注意,這是對IFS的全局更改,但只能在全局範圍內在命令替換完成後消失。

與您for循環的例子,任何時候你想要遍歷,你可能想使用一個while循環與read,而不是比一個硬編碼的列表以外的東西(包括陣列的擴展)根據Bash FAQ 001,循環爲for

把你for循環這裏,例如:

(IFS=,; for i in $(echo foo,bar,baz) ; do echo_n $i; done) 

我反而把它分割成一個數組,然後再與for迭代:

data="foo,bar,baz" 
IFS=, read -r -a items <<< "$data" 
for i in "${data[@]}"; do 
    echo_n "$i" 
done