有幾種可行的方式來實現這一目標。
如果要密切堅持你原來的版本,它可以做到這樣:
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
這仍然會失敗,如果文件名在他們的文字換行,但空間不會打破它。
但是,與IFS混合是沒有必要的。這裏是我做到這一點首選方式:
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
如果您發現< <(command)
語法不熟悉你應該閱讀有關process substitution。與for file in $(find ...)
相比,此優點是可以正確處理包含空格,換行符和其他字符的文件。這是可行的,因爲find
和-print0
將使用null
(又名\0
)作爲每個文件名的終止符,並且與換行符不同,null不是文件名中的合法字符。
的優勢,這在幾乎等效的版本
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
的是,在while循環體的任何變量賦值被保留。也就是說,如果你像上面那樣輸入到while
那麼while
的主體在一個子shell中,這可能不是你想要的。
的進程替換版本在find ... -print0 | xargs -0
的優點是最小的:該xargs
版本是好的,如果你需要的是打印線或對文件執行一個操作,但如果你需要執行多個步驟的循環版本更容易。
編輯:這是一個很好的測試腳本,這樣你就可以在解決這個問題
#!/usr/bin/env bash
dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"
touch 'file not starting foo' foo foobar barfoo 'foo with spaces'\
'foo with'$'\n'newline 'foo with trailing whitespace '
# while with process substitution, null terminated, empty IFS
getlist0() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# while with process substitution, null terminated, default IFS
getlist1() {
while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# pipe to while, newline terminated
getlist2() {
find . -iname 'foo*' | while read -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# pipe to while, null terminated
getlist3() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, default IFS
getlist4() {
for file in "$(find . -iname 'foo*')" ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, newline IFS
getlist5() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# see how they run
for n in {0..5} ; do
printf '\n\ngetlist%d:\n' $n
eval getlist$n
done
rm -rf "$dir"
這是非常乾淨。並且讓我感覺比改變IFS和for循環更合適 – Derrick
這將拆分包含\ n的單個文件路徑。好吧,那些不應該在周圍,但他們可以創建:'touch「$(printf」foo \ nbar「)」' –
@OllieSaunders:它總是一些東西! –