シンボリック・リンクを用いた分析対象データの管理ノウハウとシェルスクリプト

技術研究所のまつけんです。

技術研究所 (技研) では、画像 (静止画) データや動画データを分析対象として扱います。その際、分析対象のデータを間違えて消してしまったり、書き換えてしまったりすると大変です。今回は、私が実践している「間違いが起きない仕組み」について紹介したいと思います。具体的には、

  • オリジナルファイルを入れたディレクトリは書き込み禁止にし、
  • その隣に作業用ディレクトリを作成し、
  • オリジナルと同じディレクトリ構造を作り、
  • オリジナルファイルへのシンボリック・リンクを作成

します。もちろん、手作業でも出来るのですが、オリジナルファイルが多数ある場合などには自動化しないと大変です。そこで本記事では、そのために作成した、複数のファイルやディレクトリを扱うシェルスクリプトと、その作り方も紹介します。

例えば、動画を分析するプロジェクト「movie_analyse」において、10月20日と10月31日に合計5本の動画を撮影したとします。それらを2019-10-20、2019-10-31の2つのディレクトリ (フォルダ) に格納します。findコマンドでアイテムの一覧を表示すると、以下のようになります:

技研ではデータ分析や機械学習にUbuntu + Python 3 + Jupyter Notebookを利用しています。UNIX系OSにおいて、ファイルの書き換えはファイルのパーミッション設定、ファイルの削除はディレクトリのパーミッション設定で管理します。というわけで、

を実行して、ディレクトリとファイルの書き込み許可をオフにしてしまえば、ファイルの書き換えも削除もできなくなります。

しかし、これでは、分析結果や途中経過をmovie_analyse/2019-10-20/やmovie_analyse/2019-10-31/の中にファイルとして保存することも出来ません。ディレクトリを書き込み禁止にしているので、ファイルの作成も出来なくなってしまうからです。かと言って、ディレクトリごとコピーして、そこで作業をするとなると、ストレージが余分に消費されてしまいます。そこで、冒頭で述べた通り、

  • movie_analyseの隣にmovie_analyse.workというディレクトリを作成し、
  • そこにオリジナルと同じディレクトリ構造を作成し、
  • その中に分析対象ファイルへのシンボリック・リンクを作成

します。すなわち、

という状態です。
「movie_analyse.work/2019-10-20/movie to analyse (1).avi」は「movie_analyse/2019-10-20/movie to analyse (1).avi」を指すシンボリック・リンク、
「movie_analyse.work/2019-10-20/movie to analyse (2).avi」は「movie_analyse/2019-10-20/movie to analyse (2).avi」を指すシンボリック・リンク、
・・・・というわけです。これであれば、movie_analyse.work内のディレクトリを書き込み可に設定しておいても安心です。分析対象ファイルを書き込み不可にしておけば、間違えて書き換えてしまう心配はありませんし、シンボリック・リンクを消してしまっても、すぐに作成しなおすことが出来ます。

では早速、ディレクトリ構造のコピーから始めます (シェルはbashです)。movie_analyse内のディレクトリ一覧は「find movie_analyse -type d」で得られます:

それをsedで加工して「xargs mkdir -p」に流し込めば終わりです:

sedコマンドのセパレータは/や#や!などが使えますが、pathを扱う場合は/が出てきますので/以外を使います。#は他の文字と比べて目立つので、私のオススメは#です。sedコマンドに2つの正規表現置換を与えていますが、前者は元の名前に.workを追加するためのもの、後者は行頭と行末にダブルクォーテーションを追加するためのものです。

最終的にシェルスクリプトにするので、2つのディレクトリ名movie_analyse、movie_analyse.workは変数に格納しておきます。

sedの1つ目の引数をダブルクォーテーションに変更したのはシングルクォーテーションだと$XXXを展開してくれないからです。

続いて、シンボリック・リンクを作成してみます。手動で1つずつ作るには、

のように手打ちします。これをそのままシェルスクリプトで実現しようとすると「cd ../2019-10-31」のような戻る処理が面倒です。そのような場合、( )を使うことで簡単にできます:

では、これをシェルスクリプトにしてみましょう。ファイル一覧を取得して、それらについて1つずつ処理するには、

という方法を使います。1行で書く場合は、

となります (doの後ろにセミコロンを入れるとエラーになるので注意)。もう1つ、forを使って、

というふうに書く方法もありますが、こちらの場合、検索結果に半角スペースが含まれいていると、うまく動きません。以下のように、スペースで分割されたものがFILEに格納されてしまうからです:

ちなみに、「while read」を知る前は、こんな感じ:

で一旦、スペースをランダムな文字列に置き換えてから戻すということをやっていました。

さて、話を本題に戻しますと、シンボリック・リンクの作成をスクリプトにしたものは、以下の通りとなります:

まず、シンボリック・リンクを作成するべきディレクトリを生成し、DIRに格納します。dirnameで元ファイルの親ディレクトリの名前を得て、.workを挿入しています。続いて、そのディレクトリへのpathに含まれるスラッシュの数をカウントし、その数の分だけ../を繋げた文字列をPREFIXに格納します。なお、「sed -e ‘s#[^/]##g’」でスラッシュ以外の文字を除去していますが、「tr -cd ‘/’」でも同じ結果になります。最後にlnコマンドでシンボリック・リンクを作成します。このとき、FORCEにfを代入しておけば、既にシンボリック・リンクが存在しても強制的に作成できます。

では、実行結果を確認してみましょう:

目論見通りになっています。「chmod -R a-w $ORG」も実行して、その結果を確認します:

これで、movie_analyse.work内で何をしても安心です。試しにシンボリック・リンクに対して上書きを試みてみます:

ちゃんと拒否されました。

では、最後に最終版のシェルスクリプトmk_links.shを掲載します:

元ファイルを格納するディレクトリをmovie_analyse.orgにして作業ディレクトリをmovie_analyse.workにしたい場合のために、WRK=の行に「sed -e ‘s#.org$##’」を追加しています。

使い方は以下の3通りです:

引数の数のチェックなどは入れていませんが、必要であれば、ファイルの先頭に

を追加することで実現できます。

如何でしたか? これで明日から元データを損なうことを心配せずにデータ分析が出来ますね。

  • このエントリーをはてなブックマークに追加