LoginSignup
77
75

More than 5 years have passed since last update.

[bash] スペースが入ったファイル名の処理

Last updated at Posted at 2014-10-28

スペースを含んだファイル名を bash で操作しようとした時にハマったので、メモ。
基本はls *.txtなどの出力を文字列として扱わず、ファイルパスとして扱えば良い。

例えば、下記のような bash があったとする。

touch a.txt
touch b.txt
for file in `ls *.txt`
do
  ls "${file}"
done

このケースではちゃんと動くが、ファイル名にスペースを含んだ場合は動かなくなる。

touch I\ and\ you.txt
for file in `ls *.txt`
do
  ls "${file}"
done
# 下記のように別々のファイルとみなされる
# ls: I: No such file or directory
# ls: and: No such file or directory
# ls: you.txt: No such file or directory

なぜ動かないかと言うとls *.txtの出力が文字列として評価され、次のように展開されているから。たとえば、ls *.txtをダブルクオーテーションとかで囲んでも同じエラーになる。

for file in I and you.txt

これの解決策をふたつ。まずは単純にワイルドカードを使う例。

touch I\ and\ you.txt
for file in *.txt
do
  ls "${file}"
done

こうすれば文字列として認識されないので、1ファイル毎動く。ただ、これだと使いづらい。例えば find とかで見つけたファイルを処理する場合は、次のように行う。

touch a\ and\ b.txt
find . -name '*.txt' | while read file
do
  ls "${file}"
done

シングルクォートを含んだファイルも同様に対応可能。

#シングルクォートのハイライトが変ですね Qiita さん
touch eiji\'s.txt
find . -name '*.txt' | while read file
do
  ls "${file}"
done

しかし、ここで問題が発生する場合も。例えば、ファイル名を配列に入れたい場合に次のように記述すると失敗する。

touch a.txt
touch b.txt
touch a\ and\ b.txt
files=()
find . -name '*.txt' | while read file
do
  files+=("${file}")
done
echo "${files[@]}"

これは、スペースが入ってるとかでは無くて、パイプの前後でプロセスが別になるため、filesにファイル名が足されないのが原因。

これを解決するには、パイプ出力を現在のシェル上のwhileに喰わせる上手いやり方が参考になる。

touch a.txt
touch b.txt
touch a\ and\ b.txt
files=()
while read file
do
  files+=("${file}")
done < <(find . -name '*.txt')
echo "${files[@]}"

doneの後ろの<<が離れているのがポイント。
見てしまえば、なるほどな、と思うが、思いつかないなー。
普通にperlとかに逃げそうw。

77
75
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
75