今回はsipsで表示できるEXIF情報から必要な情報を取り出してみます。情報を取り出して取捨選択するのに便利なのがgrepです。findコマンドと組み合わせて使われることが多いgrepですが、今回は簡単なgrepの利用方法としてEXIF情報を元に画像ファイルを変換してみます。

grepってなに?

ファイルの中をコマンドから検索できるgrepは豊富なオプションを持ち、パイプ(|)処理などと組み合わせることで、多様な作業を実行してくれる

 grep (global regular expression print) はテキストから指定された正規表現にマッチするテキストを抽出します。例えばパイプなどによって渡されたテキストやfindで検索したファイルやプロセスを出力するps、各種ログファイルなどから該当する文字があるかどうか調べる場合に使われます。grepは完全一致・部分一致検索だけでなく正規表現が使えるため、より高度なテキスト検索ができます。
 grepは単独でも利用できますが、他のコマンドからの出力を利用するパイプと組み合わせると、より強力な処理を行うことができます。

 今回はEXIF情報を検索するだけなので複雑な正規表現は使いません。まずは簡単なgrepの使い方からです。
 grep単独でファイル内容を検索してみましょう。ここでは、いつものようにユーザーのデスクトップにsampleディレクトリを用意し、そこにファイルを用意しておきます。今回用意したファイル名と内容は以下の通りです。

・test1.txt
model:iPhoneSE
date:2021/7/21

・test2.txt
model:iPhoneSE2
date:2021/7/22

・test3.txt
model:iPHONE SE2
date:2021/7/22

・test4.txt
model:iPhoneXR
date:2021/9/1

・test5.txt
model:GalaxyS20
date:2021/9/1

・test6.txt
model:iPhoneSE
model:iPhoneSE2
model:iPhoneXR
model:Nexus One
date:2021/11/3

・test7.txt
model:iPhoneSE
model:GalaxyS20
model:REGZA PHONE
model:GalaxyS20
date:2022/2/22

それではgrepを使ってそれではgrepを使って簡単な文字の検索を行ってみましょう。まず、test1.txtにiPhoneの文字列があるかどうか検索してみます。grepでは以下のように指定します。test1.txtにはiPhoneの文字列があるのでマッチした行が出力(表示)されます。

grep iPhone test1.txt

今度は複数のiPhoneの文字があるtest6.txtで試してみましょう。以下のようにするとtest6.txtファイル内の検索が行われます。iPhoneの文字が含まれる行は全てマッチするのでiPhoneSE、iPhoneSE2、iPhoneXRがある行が表示されます。

grep iPhone test6.txt

今度はiPhoneの文字がないファイル(test5.txt)を検索してみます。以下のようにするとtest5.txtファイル内の検索が行われます。

grep iPhone test5.txt

test5.txtファイルにはiPhoneの文字がないので何も出力されません。

1つのファイルを検索するのではなく、まとめて検索することもできます。この場合、だいたい察しがつくと思いますが、以下のように指定します。この場合カレントディレクトリ内のテキストファイル(拡張子txtのファイル)の内容が検索されます。複数のファイルを検索した場合、ファイル名も出力されます。

grep iPhone *.txt

iPhoneの文字を検索しましたが、test3.txtはiPhoneの文字が大文字のためマッチしませんでした。大文字小文字を問わずに検索するにはオプションの-iを指定します。以下のようにするとtest3.txtもマッチするようになります。

grep -i iPhone *.txt

test3.txtはmodel:iPHONE SE2という文字列になっていますが、このiPHONE SE2にマッチさせるにはシングルクオートまたはダブルクオートで囲みます。というのも検索する文字列内に半角空白が含まれているからです。以下のようにするとカレントディレクトリにあるテキストファイル内からiPHONE SE2を含む文字列を検索します。

grep 'iPHONE SE2' *.txt

シングルクオートまたはダブルクオートで囲まないとどうなるのかというと以下のような結果になります。

grep iPHONE SE2 *.txt

grepはオプション指定の後に検索対象のファイル(ファイルパス)を複数指定できます。つまり以下のようにするとtest1.txt, test3.txt, test5.txtの3つのファイルからSEの文字を検索します。

grep 'SE' test1.txt test3.txt test5.txt

複数のファイルを検索対象にした場合、マッチした文字列の前にファイル名が表示されます。このファイル名は不要だという場合には-hのオプションを指定すると表示されなくなります。

grep -h 'SE' test1.txt test3.txt test5.txt

少しだけ正規表現

 grepが長年使われ続けるのは正規表現を利用できるからでしょう。現在のように、おおよその表記パターンが決まる前なので、grepには基本正規表現と拡張正規表現、そしてPerlの正規表現を指定できます。それぞれ以下のオプション記号を指定します。なお、オプションを省略した場合は基本正規表現を指定したことになります。

基本正規表現  -G
拡張正規表現  -E
Perlの正規表現 -P

先ほどの検索ではiPhoneSEの文字列で検索した際に、iPhoneSEとiPhoneSE2がマッチしていました。両方マッチするのではなく、iPhoneSEのみにマッチさせたい場合はどうしたらよいのでしょうか?
単純一致検索だと先例のようにうまくいきません。将来的にiPhoneSE3やiPhoneSEVENELEVENといった機種が出てきた場合もうまくマッチしないことになります。
ここで正規表現の出番です。正規表現の場合文字列にマッチさせるためには何らかの法則を見つける必要があります。逆に規則性がない文字列に対しては正規表現の出番は少ないでしょう。

さて、今回の場合iPhoneSEの文字列にマッチさせるわけですが、とりあえず何らかの法則を探すために検索対象の各ファイル内の該当行をピックアップしてみましょう。なお、ファイル名は関係ないので純粋に文字列のみで、なおかつ重複しているものは除外しています。

model:iPhoneSE
model:iPhoneSE2
model:iPHONE SE2

最後のiPHONE SE2は英大文字小文字にマッチさせるかどうかのオプション指定で区別できますので除外します。すると以下の2行になります。

model:iPhoneSE
model:iPhoneSE2

異なるのは最後の文字だけです。また、行末の文字が異なるという見方もあります。
都合のよいことに基本正規表現には行末を示す$記号があります。
そこで行末がEの場合のみマッチさせるようにしてみましょう。以下のようにすると行末がEにマッチする行が表示されます。

grep 'E$' *.txt

最後にEの文字がある機種にマッチするので期待通りの結果です。が、よく見るとiPhone以外の機種にもマッチしています。大文字のEだけでなく小文字のeにもマッチするようにオプションを指定すると、さらに他の機種にもマッチしてしまいます。

grep -i 'E$' *.txt

さすがにこれでは駄目なので、iPhoneSEのみの文字にマッチするようにしてみましょう。以下のようにすると最後の文字列がiPhoneSEにマッチするようになります。(最初からこうするのが正しいのですが、説明の都合上ということで、やや遠回し)

grep 'iPhoneSE$' *.txt

今度は期待通りiPhoneSEだけにマッチし、iPhoneSE2や他の機種にもマッチしていません。

ちなみにマッチしなかった行を出力する事もできます。この場合は以下のように-vオプションを指定します。

grep -v 'iPhoneSE$' *.txt

ただ、これだと機種名だけでなく日付の文字まで出力されてしまいます。iPhoneSE以外の機種だけにマッチさせるにはmodelで始まる行だけにマッチさせる必要があります。このような2つの条件を満たす行だけを出力するにはどうしたらよいのでしょうか。2つの条件を満たす(AND条件)場合は|(パイプ)でつなげて、もう一回grepしてしまえばOKです。つまり以下のように指定します。

grep -v 'iPhoneSE$' *.txt | grep 'model'

 1つのファイル内に複数の機種名があり、iPhoneSEとiPhoneXRの両方にマッチさせたい場合はどうしたらよいのでしょうか。この場合は以下のように-eオプションを指定すします。検索する文字列(正規表現)の前には必ず-eを指定してください。指定しないとうまく動作しません。

grep -e 'iPhoneSE$' *.txt -e 'iPhoneXR$'