複天一流:どんな手を使ってでも問題解決を図るブログ

宮本武蔵の五輪書の教えに従い、どんな手を使ってでも問題解決を図るブログです(特に、科学、数学、工学の問題についてですが)

tmp*.jpgが存在していたら消去するbashスクリクプト

天体観測の写真合成で使ったダミーファイルの消去

デジタルカメラを使った天体観測では、コンポジットと呼ばれる画像処理を行なって画質を向上させることができる。多いときは100枚近くの写真を加算平均で合成するのであるが、その時にダミーファイルがたくさん発生する。

例えば、バラ星雲の観測を行なったとしよう。Hαと呼ばれる水素原子の発光で赤く光るこの星雲は、肉眼ではみることができないし、デジタルカメラでも微かに写すのが精一杯である。

普通のデジカメでなんとか写したバラ星雲

加算平均と加算合成をほどよく組み合わせれば、細部が見事に表現された星雲の美しい姿を浮かび上がらせることが可能となる。このとき、観測写真が多ければ多いほど、高品質の写真を得ることができる。

加算平均と加算合成を組み合わせたバラ星雲の観測写真bara.jpg

「加算平均」の処理ではトーナメント方式と呼ばれる方法で2枚の画像を合成することが多く、例えば、4枚のオリジナル写真があったとすると、それは2枚と2枚の組みに分けられそれぞれ加算平均される。たとえば、最初の2枚からtmp1.jpg、次の二枚からtmp2.jpgが作られる。最後にtmp1.jpgとtmp2.jpgを加算平均、あるいは加算合成してbara.jpgを作る。

オリジナルの画像データと最終結果のbara.jpgは保存すべきだろうが、途中に生成されたtmp1.jpgとtmp2.jpgは最後には不要になる。ディスクを整理したいならば、消去することになるだろう。4枚だけなら手作業でなんとかなるが、100枚の合成をやる場合には、最初の加算平均で50枚、次のプロセスで25枚、次に12枚、6枚...と行った具合にtmp*.jpgがたくさん生成される。この例の場合は、100枚近いtmp*.jpgが発生することになる。

もちろん不要なファイルは

rm tmp*.jpg

とやって一括消去することができるのは周知のことである。

しかし、例えば、30番台のtmp3*.jpgに関してのみ、加算平均ではなく比較明合成に変更したいと仮定する。そのとき、ファイルの混在を防ぐため、tmp3*.jpgをあらかじめ消去しておきたいとしよう。ただ、先の処理で(もしかすると)tmp3*.jpgがすでに消去されてしまっていて、もう存在しないのかもしれない。もし存在しないまま

rm tmp3*.jpg

と打つと、

rm: tmp3*.jpg: No such file or directory

というメッセージが出てきて「気持ちが悪い」(べつに問題が発生したということではない。単に「気持ち悪い」だけである)。

そこで、ファイルが存在すれば消去するが、存在しなければ消去しない、というスクリプトshokyo.shを書いてみようと思い立ったのである。

今回のスクリプトで主に利用するのは、bashのif文、そして論理判定を行うtestコマンドであるから、それらについて少し確認しておこう。

shokyo.sh Ver.0.0

今回作りたいのは「消去のスクリプト」ということで、shokyo.shという名前をつけることにした。

ファイル存在の有無によって、スクリプトの仕事内容が変わるので、条件文をつかった判定が必須となる。ということで、まずはbashのif文で利用するtestというコマンドの機能を確かめよう。

bashのtestコマンドの基本的な使い方

man test

とすると、次のようなオプションが目に付く。

     -e file       True if file exists (regardless of type).

bara.jpgが存在するかどうか確認させてみる。

$ test -e "bara.jpg"

「あれ? なにも結果が出てこない」と言う前に、説明をよく読んでおく必要があった(反省)。testは返り値を(密かに)システムに与えているのだ。それをシステムから教えてもらう必要がある。直前のコマンドの返り値は

$ echo $?

で得られるのでその通りに入力すると

$ test -e "bara.jpg"; echo $?

0

という値が返ってきた。「成功」つまり"bara.jpg"は存在する、である。打ち間違えて"test -e "baraJ.jpg"などと、存在しないファイル名を指定してから"echo $?$とやると"1"つまり失敗が返ってくる。

おもしろいのは、引用符がなくても大丈夫ということである。これはmanページの説明がそうなっていたため気がついた。

$ test -e bara.jpg; echo $?

0

さて、これで準備完了である。さっそくスクリプトを書いてみることにしよう。

プログラミング

まずはtmp31.jpgが存在するかどうかを判定し、存在したら「あるよ」、なかったら「ないよ」と印字するスクリプトを書いてみたい。上で確認した事項を盛り込んでスクリプトを書いてみると、だいたいこんな感じになるだろう。

#!/bin/bash

if test -e tmp31.jpg; then
 echo "あるよ"
else
 echo "ないよ"
fi

exit 0

パーミッションを設定してから、実行してみたのがこちら。

$ chmod 755 shokyo.sh
$ ./shokyo.sh

あるよ

予想通りの結果となった。

次に、tmp31.jpg, tmp32.jpg, ....など、30番代のファイル「tmp3*.jpg」が存在するか確認してみよう。 具体的なファイル名の代わりに正規表現アスタリスク*を利用して、tmp30.jpg, tmp31, tmp32, ....などがあるかどうかを確認することにしよう。

#!/bin/bash

if test -e tmp3*.jpg; then
 echo "あるよ"
else
 echo "ないよ"
fi

exit 0

実行してみると、意外な結果となった。

$ ./shokyo.sh

./shokyo.sh: line 6: test: too many arguments
ないよ

なんだこれは?

少なくともtmp31.jpgがすでに存在しているのだから、tmp3*.jpgを指定すれば「あるよ」になるはずである。しかも、訳のわからないエラーメッセージまで出ている。bashデバッグでは、最初の行に-xをつけて実行するのが常套手段である。つまり、

#!/bin/bash -x
...

とするのである。このオプションフラグをつけて実行してみたら、なんとなくエラーの原因がわかった。

+ test -e tmp30.jpg tmp31.jpg tmp32.jpg tmp33.jpg .........

-eのオプションの後にたくさんのファイルが列挙されている。もちろん、これが狙った効果なのだが、どうやら-eが引き受けられるファイル名は一度に一つだけのようらしい。一挙にたくさんのファイル名が指定されてしまったので、"too many arguments"、つまり「引数が多すぎる」と文句が出てしまったのだ。

さて、これで問題が明白となった。すなわち、例えばファイル名に30番台の数字どれかを含むファイルがあるかどうかを確認し、一つでも合致するものがあればそれを消去する、そして合致しなければなにもしない、というスクリプトは書けるだろうか?

testコマンドの別の表現法

問題解決を目指す前に、testコマンドのもう一つの表現方法を確認しておこう。カギカッコ[ ... ]を使うというものである。ただし、カッコの中においては、カッコの横に空白を(右と左のカッコのそれぞれに)入れる必要がある。空白は目に見えないので"_"で表すとすると、

if [_-e bara.jpg_]; then
..
fi

といった感じになる。

もちろん、この表現を採用したとしても、上の問題は解決しない。

lsコマンドの利用

ファイルの存在を確認するUnixの基本コマンド、それがlsである。今問題にしているtmp3*.jpgをOSレベルで確認するときは、

ls tmp3*.jpg

とするのは(Unixユーザーなら)誰でも知っている。その出力はtest -e tmp3*.jpgとやったときと同じで、複数のファイルの名前が出力として出てしまうから、そのままでは使えない。複数の結果が出たらそれを切りはりするという手法は可能であろうが、プログラミングの手順が煩雑になってしまう。

そこでtestコマンドの成功失敗を判定したコマンド自体の返り値が、lsの場合にも使えないかどうか検討しよう。

まずは、man lsで仕様を確認してみよう。

$ man ls
...

DIAGNOSTICS
     The ls utility exits 0 on success, and >0 if an error occurs.

成功すれば0、失敗すると正の整数が出てくるという。まずは、これを利用することにしよう。

shokyo.sh ver.1.0

lsコマンドを利用して書き換えたのが次のスクリプトである。

#!/bin/bash                                                                                                                           

ls tmp3*.jpg > /dev/null
if [ $? -eq 0 ]; then
    echo "あるよ"
else
    echo "ないよ"
fi

exit 0

/dev/nullにリダイレクトすることで、ターミナルにファイル名がズラズラと印字されるのを防いでいる。逆に、ズラズラ印字させたいときは/dev/nullにリダイレクトしなければよい。実行結果は予想通りである。

$ ./shokyo.sh

あるよ

いまは「あるよ」とか「ないよ」とか印字させているが、この部分を消去コマンドなどに置き換えれば目的の機能をもったスクリプトになるだろう。