2014-10-19 38 views
0

我有一些帶有數據的製表符分隔的文件。我正在處理它一次讀取一行到一個數組中。但是,解析此命令的IFS參數存在一些嚴重問題。讀取循環時解析IFS參數的問題?如果不是爲什麼?

下面這個簡短的例子可以在幾個bash版本和幾個系統Windows/cygwin,linux和BSD之間重現。

比方說,我想打印與表狀框架的每一行(這是不相關的 - 讀取相關的):

$ IFS= echo -e "1 2 3\t4 5\t6" | while IFS=$'\t' read -r -a array ; do for field in ${array[@]} ; do echo -n "| $field " ; done ; echo "|" ; done 

結果我得到的是:

| 1 | 2 | 3 | 4 | 5 | 6 | 

經過幾次嘗試之後,獲得預期行爲的唯一方法是覆蓋全局IFS變量(爲了便於將其包裝在子外殼中):

$ echo -e "1 2 3\t4 5\t6" | (export IFS=$'\t' ; while read -r -a array ; do for num in ${array[@]} ; do echo -n "| $num " ; done ; echo "|" ; done) 
| 1 2 3 | 4 5 | 6 | 

現在的問題是:爲什麼IFS參數/變量的行爲如此怪異?它背後有沒有邏輯? - 或 - 我錯過了什麼?

+0

'用於$ {array [@]}中的字段'string-拆分字段。你需要在引號**中使用'for $ {array [@]}「**中的字段來保留邊界。 – 2014-10-20 00:08:41

+0

......這就是說你的解析是完美的,但你在輸出端搞亂了它。 – 2014-10-20 00:15:39

回答

2

你的劇本是非常接近正確的:

$ printf '%s\t' "1 2 3" "4 5" "6" | 
> while IFS=$'\t' read -r -a array ; do 
> for field in "${array[@]}" ; do 
>  printf '| %s ' "$field" 
> done 
> printf '|' 
> done 
| 1 2 3 | 4 5 | 6 | 

"${array[@]}"必須被引用,準確地保存數組邊界。

+0

Aaaand the award go here! :) - 非常感謝,現在一切都合情合理 - 在兩個例子中「對於我在$ {array [@]}中」是真正的罪魁禍首,而不是IFS本身。唯一的區別是在第二個例子中(至於循環已經重寫了IFS),它正確地分隔了數組中的字段。非常感謝。 – yatsa 2014-10-20 10:08:11

1

IFS表現完全像它應該。幾乎所有Unix類型系統上的默認IFS'space tab newline'。通常被視爲:

IFS=$' \t\n' 

在原來的情況下IFS= echo -e "1 2 3\t4 5\t6",你取消設置IFS,然後調用echo。默認應用在空間上打破。然後,您明確設置IFS=$'\t',它將字段分隔/字詞拆分正確限制爲tab字符,並獲得所需的結果。

+0

...但用戶看不到他們想要的結果 - 他們看到'| | 1 | 2 | 3 | 4 | 5 | 6',而他們期望'| 1 2 3 | 4 5 | 6',因此這個問題。 – 2014-10-20 00:18:18

+0

現在你讓我困惑。當我讀到這一點時,整個觀點就是獲得'|的驚喜1 | 2 | 3 |'而不是'| 1 2 3 |'在取消設置IFS後。 OP使用'IFS = $'\ t''獲得了期望的結果。問題是「第一次發生了什麼?」和「背後有邏輯嗎?」也許我只是變老:) – 2014-10-20 00:52:17

+0

@CharlesDuffy - 我明白你在說什麼,我同意這個問題在輸出端引用。 - 我變老了。 – 2014-10-20 01:29:32