3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【ShellScript】意地悪なファイル名があっても安心してループ処理したい

Last updated at Posted at 2022-05-24

よく見る方法

/path/to/hoge内に存在するファイル1件1件に対して何らかの処理をしたい時。
素直に書くならこんな感じ。

proc_hoge.sh
#!/bin/bash 

find /path/to/hoge -type f | while read x; do
  echo "do something...: ${x}!!"
done

問題点

ファイル名に特殊な文字が含まれていた場合、、、?

tree /path/to/hoge
# => /path/to/hoge
#    ├── 'aaa   '     :末尾に空白が含まれている
#    ├── 'bbb\012ccc' :改行コード(\012)が含まれている
#    └── 'ddd\eee'    :バックスラッシュが含まれている

ファイル名を正しく取得することができなかった。

./proc_hoge.sh
# => do something...: /path/to/hoge/bbb!!
#    do something...: ccc!!
#    do something...: /path/to/hoge/aaa!!
#    do something...: /path/to/hoge/dddeee!!

要因

findによって各ファイル名が改行区切りで出力されているが、その内容をreadが正しく取得できていない。

  • IFSの設定(デフォルトではスペース・タブ・改行)に従ってフィールドを分割するため、末尾の空白が無視されてしまっている
  • 標準入力から内容を一行ずつ読んでいるため、改行が含まれるファイル名を一度に取得できていない
  • エスケープ文字として認識されているため、バックスラッシュが無視されてしまっている

丁寧な方法

上で挙げた3つの要因を解決してあげると次のようになる。

proc_hoge.sh
#!/bin/bash

find /path/to/hoge -type f -print0 | while IFS= read -r -d $'\0' x; do
  echo "do something...: ${x}!!"
done

ファイル名が正しく取得できていることがわかる

./proc_hoge.sh
# => do something...: /path/to/hoge/bbb
#    ccc!!
#    do something...: /path/to/hoge/aaa   !!
#    do something...: /path/to/hoge/ddd\eee!!

解説

  • IFS=としてreadを実行することで、フィールド分割されなくなっている
  • findに-print0を指定することで、改行区切りでなくヌル文字\0区切りで出力されるようになる
  • readに-d $'\0'を指定することで、一行ずつでなくヌル文字\0区切りで読み取るようになる
  • readに-rを指定することで、バックスラッシュがエスケープ文字として扱われなくなる

おまけ

上記の用にパイプ|でつないでwhile文を実行している場合、
while文はサブシェルで実行されるため、外の変数を変更することができない。

#!/bin/bash

count=0

find /path/to/hoge -type f -print0 | while IFS= read -r -d $'\0' x; do
  echo "do something...: ${x}!!"
  count=$(($count + 1)) # ここでの変更はwhile文の外に影響しない
done

echo $count
# => 0

このような処理をしたいときは、代わりにプロセス置換<()を用いてあげる必要がある。

#!/bin/bash

count=0

while IFS= read -r -d $'\0' x; do
  echo "do something...: ${x}!!"
  count=$(($count + 1)) # サブシェルで実行されていないため、ここでの変更が影響する
done < <(find /path/to/hoge -type f -print0)

echo $count
# => 3

まとめ

シェルでイレギュラーなケースまで網羅するのは面倒で難しい。

3
1
0

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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?