寫 shell script 時常碰到 list 不好處理的問題,裡面如果包含空白之類的
IFS
區隔,一不小心就會發生意外。例如目錄中包含 My Music
這個目錄,
用以下的 shell script 處理 ls -1
時,就會分別印 My
跟 Music
。
1 | for f in `ls -1`; do |
解法可以用 read
讀入變數:
1 | ls -1 | while read f; do |
如果需要複雜一些的篩選,甚至處理有 \n
在裡頭的檔案,那可以用 find -print0
:
1 | find -mindepth 1 -maxdepth 1 -print0 | \ |
但這又有個討厭的地方,就是在迴圈中如果需要用到 stdin
就出錯了,因為
stdin
是接到 find
的 stdout
。解法會動用到 process substitution 跟
exec redirection 用來改變環境的 file descriptor 設定。
1 | exec 3< <(find -mindepth 1 -maxdepth 1 -type d -print0) |
主要差異是第一行,<(COMMAND)
的用意是把 find
的輸出接到一個 named
pipe 或者是 /dev/fd
之下當成一個檔案(詳情請參閱 info bash
),而 exec 3<
的意思則是用 fd 3 打開這個檔案。下一行 read
後面接的 <&3-
是把 fd 3
move 到 read 的 stdin,這樣就不會影響到 loop 中的 stdin 了。