来世から頑張る!!

技術ブログを目指して

一般的なShellで空白を含むPathをループしたい願望

まず、Shellって何かがわからないところから入るわけだけど、 いや、自分が打っているのが所謂shなのかbashなのかzshなのかがわからないってことなんだけど、 まあ複数ファイルを順次処理したい時ってあるわけですね。

forを使ったループ

シンプルな例だと以下のように書くわけです。

  for f in path/to/target/*.txt
  do
    echo "file is: $f"
  done

このディレクトリーにa.txt, b.txt, c.txtがあれば、以下のように表示されるでしょう。

file is: path/to/target/a.txt
file is: path/to/target/b.txt
file is: path/to/target/c.txt

スペース(など)が含まれたパス

ところが、スペースが含まれているようなディレクトリー名だと、これがうまく動かないのです。

ディレクトリー名をpath/to target with space/だとしてみましょう。

ls "path/to target with space/"
a a.csv      b c d.csv

こんな場合ですね。

同じようなループを実行すると、当然うまく行きません。

for f in path/to target with space/*.csv
do
  echo "file is: $f"
done

結果はこんな感じになったりしてしまいます。

file is: path/to
file is: target
file is: with
file is: space/*.csv

解決方法1: IFS組み込み変数を使用

どうにかスペース入のパスをループできないのかなーと検索した結果見つけたのが、 組み込み変数IFSfindコマンドを組み合わせる方法です。

IFSを変更すると、shellが扱う区切り文字を変更できるとこのことです。

まずfindを実行すると、以下のような結果が取得できます。

> find "path/to target with space" -type f -name "*.csv"
path/to target with space/a a.csv
path/to target with space/b c d.csv

結果が改行されて出てくるので、IFSに改行記号\nを設定後ループするとうまくいくようです。
ただし、IFSを元に戻せるように、退避してから実行した方が良いようです。

IFS_BACK="$IFS"
IFS=$'\n'
for f in `find "path/to target with space" -type f -name "*.csv"`
do
  echo "file is: $f"
done
IFS="$IFS_BACK"
file is: path/to target with space/a a.csv
file is: path/to target with space/b c d.csv

解決方法2: ""でくくる

とりあえず上をコピペして使っていたのですが、 今日もっと簡単にできる方法を発見してしまったのでそちらに書き換えることにしました。

皆さんご存知のように、shellではダブルクォートに囲まれた部分は1つとみなしてくれるのです。
ただし、中にある*は展開してくれません。

まず、ダメな例。

for f in "path/to target with space/*.csv"
do
  echo "file is: $f"
done

これの結果は以下のようになります。

file is: path/to target with space/*.csv

*がそのままですね。

すごく単純すぎて気づかなかったのですが、 *をダブルクォートの外に出してあげれば展開されるんですよね、これ。

for f in "path/to target with space/"*".csv"
do
  echo "file is: $f"
done
file is: path/to target with space/a a.csv
file is: path/to target with space/b c d.csv

どこでダブルクォートを終わらせてもいいみたいなので、変数の部分だけくくるとかが現実的なのかなという感じです。

BASE_PATH="path/to target with space"
for f in "$BASE_PATH"/*.csv
do
  echo "file is: $f"
done

結論

shell難しい。

自分のshell力の低さを痛感させられた。