非常に簡単な解決策もあります:bash グロビングに頼る
$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid file 3"
$ ls
stupid file 3 stupid file1 stupid file2
$ for file in *; do echo "file: '${file}'"; done
file: 'stupid file 3'
file: 'stupid file1'
file: 'stupid file2'
この動作がデフォルトの動作であるかどうかはわかりませんが、shopt に特別な設定が表示されないため、「安全」である必要があると言うことに注意してください (osx と ubuntu でテスト済み)。
これを実現するには、いくつかの実行可能な方法があります。
元のバージョンに厳密に固執したい場合は、次の方法で行うことができます:
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)
が見つかった場合 構文に慣れていない場合は、プロセス置換について読む必要があります。 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
の本体 あなたが望むものではないかもしれないサブシェルにあります.
find ... -print0 | xargs -0
に対するプロセス置換バージョンの利点 最小です:xargs
ファイルに対して 1 行を印刷するか、1 つの操作を実行するだけであれば、バージョンは問題ありませんが、複数のステップを実行する必要がある場合は、ループ バージョンの方が簡単です。
編集 :これは、この問題を解決するためのさまざまな試行の違いを理解できるように、優れたテスト スクリプトです。
#!/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"
単語ベースの反復を行ベースの反復に置き換えることができます:
find . -iname "foo*" | while read f
do
# ... loop body
done