grep -l で取得したファイル名リストを for in で回す
Bash の話。
grep -l
コマンドで、検索文字列を含むファイル名のみを出力できる。こんな感じだ。
# -r オプションは指定ディレクトリ配下を再帰的に検索する
$ grep -lr '検索文字列' './テスト ディレクトリ/Texts'
./テスト ディレクトリ/Texts/Test1.md
./テスト ディレクトリ/Texts/Test2.md
./テスト ディレクトリ/Texts/Test3.md
で、この結果を配列として解釈させ、for in
で回そうとしていた。
file_names=( $( eval grep -lr '検索文字列' './テスト ディレクトリ/Texts' ) )
for file_name in "${file_names[@]}"; do
echo "${file_name}"
done
普通のパスであれば特に問題にならないのだが、「テスト ディレクトリ
」のように、半角スペースを含むパスを含んでいると、この部分で区切られてしまうのだ。ファイルが3つヒットしていた場合だと、要素数が 3 ではなくて 6 と解釈されてしまうワケだ。
じゃあ、ファイルごとのパスをダブルクォートかなんかで囲んでやろうと思い、
file_names=( $( eval grep -lr '検索文字列' './テスト ディレクトリ/Texts' | awk '{ print "\"" $0 "\"" }' ) )
こんな風にして、
"./テスト ディレクトリ/Texts/Test1.md"
"./テスト ディレクトリ/Texts/Test2.md"
"./テスト ディレクトリ/Texts/Test3.md"
といった結果が得られるようにしたのだが、なぜかコレでもスペース部分で区切られてしまった。
- 参考 : まアンジー on Twitter: "一度ダブルクォーテーションでくくって、 eval で戻してみました。" … 自分は
sed
でダブルクォートを付与しようとしていたが、awk
という手もあった。
for x in `grep -l foobar * | awk '{print "\"" $0 "\""}'`
do
eval echo $x
done
色々試行錯誤した結果、$IFS
を一時的に変更することで上手くいった。
# 元の $IFS を退避する
ORIGINAL_IFS=$IFS
# 改行コードを指定する
IFS=$'\n'
# 検索処理を配列に控える
file_names=( $( eval grep -lr '検索文字列' './テスト ディレクトリ/Texts' ) )
# $IFS を元に戻す
IFS=$ORIGINAL_IFS
# コレで、パスにスペースを含んでいても上手く読み取ってくれる
for file_name in "${file_names[@]}"; do
echo "${file_name}"
done
コレで OK。grep -l
コマンドの結果は \n
で改行されているように見えて、実際は \n
とは違うようだ。$IFS
周りは全然分からない。