2013-10-04 56 views
6

我試圖在shell中使用for來用空格遍歷文件名。我在stackoverflow question中讀到IFS=$'\n'可以使用,似乎工作正常。然後我讀了in a comment to one of the answers,爲了讓它可以和bash以外的shell兼容,可以使用IFS=$(echo -e '\n')

前者有效,後者則不。評論多次提高。我檢查了輸出和變量似乎並不包含換行符。

#!/bin/sh 

IFS=$(echo -e '\n') 
echo -n "$IFS" | od -t x1 
for file in `printf 'one\ntwo two\nthree'`; do 
    echo "Found $file" 
done 

echo 

IFS=$'\n' 
echo -n "$IFS" | od -t x1 
for file in `printf 'one\ntwo two\nthree'`; do 
    echo "Found $file" 
done 


輸出顯示字段分隔符缺少第一:

0000000 
Found one 
two two 
three 


但是在第二合適的:

0000000 0a 
0000001 
Found one 
Found two two 
Found three 


我發現了一個問題,回答可能會或可能不會是我的問題的重複:
linux - When setting IFS to split on newlines, why is it necessary to include a backspace? - Stack Overflow

有沒有做什麼的存在,IFS=$(echo -en '\n\b')討論什麼危險?因爲這增加了一個\b IFS(我檢查)。 \b可以在文件名中出現嗎?我不想讓我的代碼錯誤地分割文件名。

你也有任何建議如何正確處理for循環後恢復IFS?我應該將它存儲在一個臨時變量中,還是在子shell中運行IFS和for語句? (如何做到這一點?)謝謝

回答

6

更新 - 改變了我的僞評論到真正的答案。我想this answer應該解釋你所看到的行爲。具體的命令替換運算符$()和反引號將從命令輸出中去除尾隨的換行符。但是,第二個示例中的直接分配不會執行任何命令替換,因此按預期工作。

所以我不敢說我​​認爲你提到的upvoted評論是不正確的。


我認爲恢復IFS最安全的方法是將其設置在一個子shell中。所有你需要做的就是把相關的命令在括號()

(
    IFS=$'\n' 
    echo -n "$IFS" | od -t x1 
    for file in `printf 'one\ntwo two\nthree'`; do 
     echo "Found $file" 
    done 
) 

過程調用子shell會帶來短暫的延遲,所以需要考慮這是否是重複多次的性能。


另外,要非常小心,文件名可以同時包含\ b和\ n。我認爲他們不能包含的唯一字符是斜線和\ 0。至少這就是它在這個wikipedia article上說的。

$ touch $'12345\b67890' 
$ touch "new 
> line" 
$ ls --show-control-chars 
123467890 new 
line 
$ 
4

由於新行是由命令替換剝離,作爲@DigitalTrauma正確地寫,你應該使用一個不同的,符合POSIX標準的方式。

IFS=' 
' 

寫入換行符並將其分配給它。簡單而有效。

如果您不喜歡跨兩行的作業,可以使用printf作爲替代選項。

IFS="$(printf '\n')" 
+0

謝謝你的信息我會記住這一點 – user2672807

+0

請刪除您的第二個例子,因爲它是不乾淨: 'IFS = 「$(printf的 '\ n +')」' '$回聲 - n「$ IFS」| xxd' '00000000:0a2b' < - 這也給IFS增加了'+'。 –

+0

@TomHale我刪除了不需要的+ –