pslaboが試したことの記録

はてなダイヤリーからはてなブログに引っ越してきました

この日記は現在実行中の減量記録を含む個人的なメモとして始めましたが、最近はコンピュータやガジェット、ハック、セキュリティネタのほうがメインになっております。

はてなダイヤリー時代はカテゴリ分けが適当だったのですが、これはそのうち直します。


MacOS X で Raspberry Pi 用の起動イメージファイルをSDカードに書き込む際の一連の作業を自動化するスクリプト

MacOS XRaspberry Pi 用の起動イメージファイルをSDカードに書き込む際には、デバイスを umountdisk したり、dd したりするわけですが、この手の作業はデバイス名を間違えるといろんな意味でアウトですよね。

なので、この手の一連の作業を自動化するスクリプトを書いてみました。このスクリプトでは以下の流れを自動化しています。

  • 作業開始前(SDカード装着前)のデバイス一覧を取得する
  • SDカード装着後のデバイス一覧を取得して作業前の状態との差分を取り、対象のデバイス名を検出する。
  • diskutil umountdisk を実行して書き込み準備する。
  • バイス名を rdisk に置換したデバイスに対して dd で書き込みする。pv がインストール済みの場合は進捗表示のために pv 経由で書き込む。
  • 書き込み完了後に diskutil eject を行い、SD カードを抜いてよい状態にする。

こちらは実行例。

$ ./write_img2sd.sh OSMC_TGT_rbp2_20150519.img 
SDカードを装着して enter を押してください
SDデバイス名を検出しました: /dev/disk2

SDカードへの書き込み準備を行います。プロンプトが表示された場合はログインパスワードを入力してください。
Password:
Unmount of all volumes on disk2 was successful
 256MiB 0:00:53 [4.81MiB/s] [=======================================================================>] 100%            
0+4096 records in
0+4096 records out
268435456 bytes transferred in 53.217872 secs (5044085 bytes/sec)
書き込み完了しました

Disk /dev/disk2 ejected
SDカードデバイス /dev/disk2 を取り外してください。


スクリプト本体はこちらです。ただし、このスクリプトは sudo をする処理があります。内容の妥当性を適切に評価判断した上でご利用ください。

#!/bin/bash
#
# write_img2sd.sh

if [ $(uname) != "Darwin" ]; then
    echo "このスクリプトは MacOS 専用です。処理を終了します。"
    exit 1
fi

# 終了時に一時ファイルを消すための仕込み
trap do_exit 0 1 2 3 13 15

########################################
# 主処理部
do_main() {
    targetfile=$1

    # 書き込み対象のファイルが存在することを確認する
    file_existcheck "$targetfile"

    # 作業開始時点(SDカード装着前)のドライブ一覧を取得する
    get_drivelist_prev

    echo    "本スクリプトの実行前にSDカードを装着していた場合は、カードを抜いてからスクリプトを再実行してください。"
    echo -n "SDカードを装着して enter を押してください"
    read

    # SDカード装着後のドライブ一覧を取得する
    get_drivelist_post

    # SDカードのドライブを検出する
    detect_SD_device

    # SDカードのデバイス名を取得する
    sd_device=$( get_SD_devicename )
    echo "SDデバイス名を検出しました: $sd_device"
    echo ""

    # SD カードへのディスクイメージ書き込みを行う
    write_image $targetfile $sd_device

    # デバイスを取り外し可能な状態にする
    detach_device $sd_device

}

########################################
# ファイルの有無をチェックする
file_existcheck() {
    if [ "$1" == "" ] || [ ! -e $1 ]; then
        echo "ファイル $1 が見つかりません。処理を終了します。"
        exit 1
    fi
}

########################################
# 作業開始前のドライブ一覧を取得する
get_drivelist_prev() {
    file_diskutil_info_prev=$( mktemp )
    diskutil list | awk '$1 ~ /^\// { print $1 }' > $file_diskutil_info_prev
}

########################################
# 作業開始中のドライブ一覧を取得する
get_drivelist_post() {
    file_diskutil_info_post=$( mktemp )
    diskutil list | awk '$1 ~ /^\// { print $1 }' > $file_diskutil_info_post
}

detect_SD_device() {
    diff $file_diskutil_info_prev $file_diskutil_info_post > /dev/null
    if [ $? -eq 0 ]; then
        echo "SDカードを認識できません。カードを所定の手順で抜いた後、最初からやり直してください。"
        exit 1
    fi
}
get_SD_devicename() {

    sort $file_diskutil_info_prev $file_diskutil_info_post | uniq -u
    rm $file_diskutil_info_prev $file_diskutil_info_post
}

write_image() {
    which pv > /dev/null
    if [ $? -eq 0 ]; then
        pvcat="pv"
    else
        pvcat="cat"
    fi

    rdevice=$( echo $2 | sed s#/dev/disk#/dev/rdisk#g )

    echo "SDカードへの書き込み準備を行います。プロンプトが表示された場合はログインパスワードを入力してください。"
    #echo "sudo diskutil umountdisk $rdevice"
    sudo diskutil umountdisk $rdevice

    echo "$pvcat $1 | sudo dd of=$rdevice bs=1m"
    $pvcat $1 | sudo dd of=$rdevice bs=1m
    sync;sync;sync
    echo "書き込み完了しました"
    echo ""
}

detach_device() {
    diskutil eject $1
    echo "SDカードデバイス $1 を取り外してください。"
}

do_exit() {
    for file in $file_diskutil_info_prev $file_diskutil_info_post ; do
        if [ -e $file ]; then
            rm $file
        fi
    done
}

do_main "$@"