背景
iPhone を何台も持っていると、iPhone の数だけバックアップが作成され、Mac のディスク容量が圧迫されます。
自分の MacBook Air では、iPhone のバックアップファイルは、33 GB の容量でした。写真やビデオを撮りまくる人は、もっとディスク容量が多いと思います。
MacBook-Air:~ username$ source_path="${HOME}/Library/Application Support/MobileSync"
MacBook-Air:~ username$ du -hs "${source_path}"
33G /Users/username/Library/Application Support/MobileSync
そのため、iPhone のバックアップを外付けドライブに移動させます。 ( お金がある人は、iCloudを契約してください。その方が安全で楽ちんです。)
そして、本記事で記載している行為は、Apple さん曰く、「厳禁」の行為です。絶対に実施しないでください。
「Backup」フォルダはコピーはできますが、別のフォルダ、外付けドライブ、またはネットワークドライブへの移動は厳禁です。
Apple 「iPhone、iPad、iPod touch のバックアップを探す」より (2018年11月18日)
iPhone のバックアップを外付けドライブに移す
iPhone のバックアップファイルを外付けドライブに移す作業は、以下の流れになります。iPhone のバックアップが消えると大変なので、バックアップファイルの移動は丁寧かつ慎重に確認します。
- iPhone のバックアップファイルを確認
- バックアップファイルの移動先を確認
- バックアップファイルの移動
1. iPhone のバックアップファイルを確認
iPhone のバックアップファイルは、以下のフォルダに格納されています。パスの途中にスペースがあるのが曲者で、コイツのせいでソースコードが少しずつ長くなります。
/Users/<username>/Library/Application Support/MobileSync/Backup"
バックアップファイルがちゃんと存在するか、ディレクトリのパス指定が間違っていないかを確認するため、ls
と du
でバックアップファイルを確認します。
MacBook-Air:~ username$ source_path="${HOME}/Library/Application Support/MobileSync"
MacBook-Air:~ username$ ls "${source_path}"
Backup
MacBook-Air:~ username$ du -hs "${source_path}"
33G /Users/username/Library/Application Support/MobileSync
ちなみに、バックアップファイルのディレクトリ構造は、以下のようになっています。
MacBook-Air:~ username$ tree -L 2 "${source_path}"
/Users/username/Library/Application Support/MobileSync
└── Backup
├── <guid1>
├── <guid2>
└── <guid2>-<yymmdd>-<hhmmss>
3 directories, 0 files
補足1 : No such file or directory
が出た場合
パスの指定が間違っていたら、以下のエラーが出力されます。また、変数をダブルコーテーション "
で囲っていない場合も、同様のエラーが出ます。"${source_path}"
を、目を皿のようにして確認しましょう。
MacBook-Air:~ username$ ls /hoge
ls: /hoge: No such file or directory
補足2 : Operation not permitted
が出た場合
macOS 10.14 Mojave にアップデートしていた場合は、以下のエラーが出力されます。
MacBook-Air:~ username$ ls "/Users/username/Library/Application Support/MobileSync"
ls: MobileSync: Operation not permitted
このエラーは、コマンドの先頭に sudo
を付けても解決しません。これは Mojave から導入された新しいプライバシー保護のせいです1。
2. バックアップファイルの移動先を確認
次はバックアップファイルの移動先を確認します。外付けドライブ (USB メモリや外付けHDD) を Mac に接続すると、以下のパスにマウントされます。
/Volumes/<外付けドライブのデバイス名>
そのため、iPhone のバックアップファイル移動先として、以下のディレクトリを作成します。
/Volumes/<外付けドライブのデバイス名>/MobileSync
ディレクトリの作成は、以下のように行います。
# 移動先のデバイス名を確認
MacBook-Air:~ username$ ls /Volumes/
Macintosh HD <device_name>
Preboot
# デバイス名の確認
MacBook-Air:~ username$ device_name="/Volumes/<device_name>"
MacBook-Air:~ username$ ls "${device_name}"
MacBook-Air:~ username$ dest_path="${device_name}/MobileSync"
MacBook-Air:~ username$ mkdir -p "${dest_path}"
MacBook-Air:~ username$ ls "${dest_path}"
3. バックアップファイルの移動
iPhone のバックアップファイルを移動します。iPhone のバックアップを移動する前に $source_path
と $dest_path
が、想定通りのディレクトリ構成か確認します。
# $source_path と $dest_path が、想定通りの構成か確認
function can_migrate_iphone_backup () {
# $source_path が存在するか確認
if [ ! -d "${source_path%/}" ]; then
echo -n "\$source_path does NOT exists"
echo " : \"${source_path}\""
echo "Please set \$source_path to correct value."
return 1
fi
# $source_path 内に Backup フォルダが存在するか確認
if [ ! -d "${source_path%/}/Backup" ]; then
echo -n "Backup folder does NOT exists in source folder"
echo " : \"${source_path%/}/Backup\""
echo "Please set \$source_path to correct value."
return 1
fi
# $dest_path が存在するか確認
if [ ! -d "${dest_path%/}" ]; then
echo -n "\$dest_path does NOT exists"
echo " : \"${dest_path}\""
echo "Please set \$dest_path to correct value."
return 1
fi
# $dest_path 内に Backup フォルダが存在しないことを確認
if [ -d "${dest_path%/}/Backup" ]; then
echo -n "Backup folder exists in destination folder."
echo -n "Backup folder should NOT exist in destination folder."
echo " : \"${dest_path%/}/Backup\""
echo "Please set \$dest_path to correct value."
return 1
fi
echo "\$source_path and \$dest_path is NO ploblem for migrate."
}
can_migrate_iphone_backup
上記の can_migrate_iphone_backup
を実行し、実行結果が"$source_path and $dest_path is NO ploblem for migrate."
と出力されたら、$source_path
と $dest_path
に問題はありません。
問題がないことが確認できたら、 iPhone のバックアップファイルをコピーします。もちろん、コピー前に iTunes は終了させます。
# バックアップファイルをコピーする前の状態確認
function show_folder_size_and_file_count () {
find "${1%/}" -type f -ls | awk 'BEGIN { sum=0;count=0 }; { sum+=$7;count++;printf "\rtotal %d Byte, %d files",sum,count }; END{ print "" }'
}
show_folder_size_and_file_count "${source_path%/}/Backup"
show_folder_size_and_file_count "${dest_path%/}/Backup"
# (テスト実行)バックアップファイルのコピー
rsync -ahvn --stats "${source_path%/}/Backup" "${dest_path}"
# (本番実行)バックアップファイルのコピー
rsync -ahv --stats "${source_path%/}/Backup" "${dest_path}"
# バックアップファイルをコピーした後の状態確認
show_folder_size_and_file_count "${source_path%/}/Backup"
show_folder_size_and_file_count "${dest_path%/}/Backup"
バックアップファイルをコピーしたら、 $source_path
側のバックアップファイルの名前を変更して一時的に退避します。そして、コピーした $dest_path
側のバックアップファイルへのシンボリックリンクを $source_path
内に作成します。
# バックアップフォルダ退避前の状態確認
show_folder_size_and_file_count "${source_path%/}/Backup"
show_folder_size_and_file_count "${source_path%/}/Backup_tmp"
# バックアップフォルダの退避
mv "${source_path%/}/Backup" "${source_path%/}/Backup_tmp"
# バックアップフォルダ退避後の状態確認
show_folder_size_and_file_count "${source_path%/}/Backup"
show_folder_size_and_file_count "${source_path%/}/Backup_tmp"
# シンボリックリンクの作成
ls "${source_path%/}"
ln -s "${dest_path%/}/Backup" "${source_path}"
ls "${source_path%/}"
# シンボリックリンクの削除 (想定外の挙動になったとき)
# unlink "${source_path}"
iTunes を起動し、 iPhone のバックアップファイルが正常に表示されていれば、移動は**「ほぼ」**完了です。
最後に退避していたバックアップファイルを削除すれば、バックアップファイルを外付けドライブに移動する作業は**「完了」**です。お疲れ様でした。
ls "${source_path%/}/Backup_tmp"
rm -r "${source_path%/}/Backup_tmp"
ls "${source_path%/}/Backup_tmp"
bash のプログラミングで注意すること、つまづいたこと、落とし穴
1. ファイルやディレクトリのパスを格納する変数は、"${value}"
と書く
この様な書き方にする理由は、以下の2つである。
- スペースが入った変数を関数の引数にすると、変数が 2 つの引数と認識される
- ダブルコーテーション内で変数と文字列を結合するとき、どこまでが変数がわかりにくい
1. スペースが入った変数を関数の引数にすると、変数が 2 つの引数と認識される
$value
にスペースを含むパスを設定した場合、<コマンド> $value
が意図しない挙動をする。コマンドの引数として $value
を 1 つ渡したつもりが、 スペースで区切られた複数の引数をコマンドに渡したことになる。そのため、変数をダブルコーテーションで囲む。
MacBook-Air:tmp username$ tree
.
└── hoge\ hoge
└── piyo
2 directories, 0 files
MacBook-Air:tmp username$ path="hoge hoge/piyo"
MacBook-Air:tmp username$ ls "$path"
MacBook-Air:tmp username$ ls $path
ls: hoge: No such file or directory
ls: hoge/piyo: No such file or directory
2. ダブルコーテーション内で変数と文字列を結合するとき、どこまでが変数がわかりにくい
$value
と $valuet
の 2 つの変数が存在する場合、"$valuetmp"
は、どっちの変数か少し悩む。そのため、どこまでが変数名か迷わないようにするために "${value}tmp"
と、括弧で囲む。
2. du のブロックサイズ表示、ディレクトリのファイルサイズ表示
バックアップファイルのコピー後、コピー元とコピー先のディレクトリサイズを比較しようとした。しかし、ls -l
では、フォルダ内のファイルサイズの合計を表示していなかった。
-
ls -l
コマンドの補足-
ls -l
コマンドを実行して、1行目に表示される total は、ディレクトリ内のファイル (ディレクトリは含まない) のブロックサイズ -
ls -l
コマンドを実行して、ディレクトリの右に表示されるサイズは i-node のブロックサイズ
-
そのため、du
でディレクトリのサイズを比較したが、du
で表示されたサイズがコピー元とコピー先で異なっていた。これは du
が、ファイルサイズではなくブロックサイズを表示しているためだった。
そのため、find
と awk
を使って、地道にディレクトリのサイズを集計するコマンドを作成した。
function show_folder_size_and_file_count () {
find "${dest_path}" -type f -ls | awk 'BEGIN { sum=0;count=0 }; { sum+=$7;count++;printf "\rtotal %d Byte, %d files",sum,count }; END{ print "" }'
}
macOS の find
コマンドには、-b
オプションが存在しないため、ネット上のコマンド例3,4,5はそのまま動かず、地味に困った。また、コマンド作成直後は、 find
コマンドの -ls
オプションや -exec
オプションを知らず、ls
や xargs
コマンドをパイプして実装する、という回り道をしてしまった。
3. man を テキストファイルに保存する方法
col
を使う。
find
, du
, ls
のオプションは、GNU 版と macOS 版でそこそこ違っていた。そのため、マニュアルをしっかり読み込むんだ。やはり、man
コマンドは偉大だった。
man find | col -bx > man_find.txt
あと、インターネットでから利用可能な FreeBSD の日本語マニュアルを以下に列挙する。
-
FreeBSD 日本語マニュアル検索
- ちゃんと動いている (2018年11月18日時点)
-
FreeBSD 用の日本語マニュアルを作るプロジェクト
-
Internal Server Error
が出る。(2018年11月18日時点)
-
なぜ macOS なのに、 FreeBSD のマニュアルを見るのかと言うと、 macOS は、Darwin (Mach Kernel + FreeBSD) を利用した OS だからである。実際、macOS の man ls
コマンドを実行すると、最初の行や末尾に BSD の文字が表示される。
MacBook-Air:tmp username$ man ls | sed -n 2p
LS(1) BSD General Commands Manual LS(1)
MacBook-Air:tmp username$ man ls | tail -n 1
BSD May 19, 2002 BSD
4. ファイルのコピー状況を把握する方法
大量 (数万個程度) のファイルをコピーする際、ファイルのコピーが、数万個中の何個完了したのか知りたかった。しかし、初期搭載のコマンドで把握する方法はなかった6。
5. bash のエスケープ文字 \
を入力する方法
bash の エスケープ文字は \
と知っていたが、入力方法が分からず非常に困った。まさか option
+ ¥
の同時押しとは...7。知らなかった。
-
\
の入力方法 :option
+¥
-
macOS 10.14 Mojaveでは新しいプライバシー保護機能のため、ホームディレクトリ以下でもターミナルからアクセスするさいには「フルディスクアクセス」設定が必要に。
そのため、以下のようにターミナルにフルディスクアクセス権限を割り当てることで、このエラーを回避します2。 ↩