寫 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 了。