BLOGサブスレッドの日常

2016.10.14

シェルスクリプトで日付ごとのフォルダを作ってファイルを整理する

chao

はじめに

金曜担当のしみずです。どうぞよろしく。

家で使っているビデオカメラの動画が溜まってきたのでバックアップしようと思い
専用ソフトでバックアップをしていたのですが、専用ソフトでは
元ファイル+mp4に変換したファイルがコピーされ、非常に時間がかかってしまっていました。

mp4変換に時間がかかるのですが、バックアップ時にそれをOFFもできなさそうでしたので
自分でバックアップのシェルスクリプトを作ることにしたので、その時にしたことを書いていこうと思います。

環境

MAC OS X El Capitan
10.11.6(15G31)

仕様

こんな仕様で作りました。

  • スクリプトの第一引数にコピー元のディレクトリ、第二引数にコピー先のディレクトリを指定する
  • コピー元のディレクトリにあるファイル全てが対象となる
  • コピー先ディレクトリ配下に、コピーするファイルのタイムスタンプで年月日のフォルダ(yyyy-MM-dd形式)を作ってそこにコピーする

タイムスタンプでフォルダを作る理由は、専用ソフトがそうなってたからです。
既にバックアップしたファイルがいくつかあったので仕様を合わせました。

ちなみに、自分で使う用だったので特に細かいエラーチェックはしていません。

スクリプト全文

以下のような感じになりました。
ひとつずつ説明していきます。

backup.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/sh
SRCDIR="$1"
DSTDIR="$2"

for filename in `ls -1 "${SRCDIR}"`
do
    srcpath="$SRCDIR/$filename"
    foldername=`ls -lT "$srcpath" | awk '{print sprintf("%04d-%02d-%02d", $9, $6, $7);}'`
    folderpath="$2/$foldername"
    mkdir -p "$folderpath" # 2>/dev/null

    dstpath="$folderpath/$filename"

    if [ ! -e "$dstpath" ]; then
        cp -av "$srcpath" "$dstpath"
    fi
done

引数を取る

第一引数であれば「$1」
第二引数であれば「$2」のような感じで取ることができます。
$0 からじゃないので注意。

SRCDIR="$1"
DSTDIR="$2"

このように、引数を別名として変数に入れて用途が分かるようにしました。
パスにスペースが含まれるかもしれないので、ダブルクォートでくくっています。
変数を参照するときは、$を付けて

$SRCDIR
$DSTDIR

のように行います。

ちなみに変数宣言は必要ないですが、変数に値を入れる際は「=」の前後にスペースを入れてはいけないようです。

指定フォルダのファイル名を一つずつ取り出す

for文を使って以下のように行います。
$SRCDIR が指定フォルダなので

for filename in `ls -1 "${SRCDIR}"`
do
    # 取り出したファイル名を表示
    echo $filename
done

これで指定フォルダのファイル一覧が表示されます。
$filename にファイル名が入っているということです。
ここで取れてくるファイル名にパスは含まれません。

対象のファイルのタイムスタンプから「yyyy-MM-dd」形式の文字列を取り出すコマンドを作る

まずはタイムスタンプを取り出すコマンドです。

$ ls -lT hoge.txt
-rw-r--r--  1 shimizu  staff  277  4  5 10:35:38 2016 hoge.txt

オプションの l はリストで一覧表示、Tはタイムスタンプを年月日で表示するものです。

ここから「yyyy-MM-dd」形式でフォルダを作らなければいけません。
まずは、上記のコマンド結果から「yyyy-MM-dd」形式の文字列を作りましょう。
色々な文字列を操作できる awk というコマンドを使います。

$ ls -lT hoge.txt | awk '{print sprintf("%04d-%02d-%02d", $9, $6, $7);}'
2016-04-05

これで、「yyyy-MM-dd」形式の文字列が作れました。
| で繋げていますが、これは ls -lT hoge.txt の結果の文字列に対して
awk コマンドで加工しますよ。ということです。

awk コマンドでは1文字以上のスペース(またはタブ)ごとに文字列を分割して加工ができます。

awk '{print sprintf("%04d-%02d-%02d", $9, $6, $7);}'

というコマンドの最後で $9, $6, $7 とありますが
ls コマンドの結果の
「-rw-r--r-- 1 shimizu staff 277 4 5 10:35:38 2016 hoge.txt」
という文字列を1文字以上のスペースで分割すると
9番目が年、6番目が月、7番目が日になります。

それを sprintf("%04d-%02d-%02d") で「yyyy-MM-dd」形式として出力したということです。

作ったコマンドを使って、日付のフォルダを作るスクリプトを書く

コマンドラインの実行結果を変数に入れる。
その変数を使って mkdir コマンドでフォルダを作る。
という事をすれば良いです。

# コマンドラインの結果を変数(foldername)に入れる
foldername=`ls -lT "$srcpath" | awk '{print sprintf("%04d-%02d-%02d", $9, $6, $7);}'`
# ファイル名だけなので出力ディレクトリを繋げてフルパスにする
folderpath="$2/$foldername"
# mkdirコマンドでフォルダを作る
mkdir -p "$folderpath" 2>/dev/null

上記のような形のスクリプトとなります。
mkdir の -p オプションでサブディレクトリも作成できるようにしています。
また、mkdir の最後に 2>/dev/null で、既にフォルダ生成済みでもエラー表示されないようにしています。

同名ファイルが存在しない場合のみコピーする

既に専用ソフトでいくつかバックアップしてしまっていたので、バックアップ済みのものはコピーしないようにしました。

# if文でファイル存在チェック
if [ ! -e "$dstpath" ]; then
    # 存在しなければコピー
    cp -av "$srcpath" "$dstpath"
fi

cpコマンドの -v オプションで、コピーするファイル名を表示するようにしてコピーの過程が分かるようにしています。

おわりに

1日かかって10ファイルくらいしかバックアップできていなかったのが
このスクリプトによって、数時間でバックアップ完了することができました。
珍しく自分用に役に立つものを作った気がします。

週刊、ウチのネコは今日もお休みです。
最近、写真を撮ってないので撮らねば。

この記事を書いた人

chao