
自作Macアプリを別のMacへまとめて入れる .command を作りました
自分用のMacアプリを少しずつ作っていたら、いつの間にか30本になっていました。
ふだん使っているMacでは、ビルドするたびに /Applications へ入れ直しているので困りません。困るのは、ほかのMacを設定するときです。
「どれが最新版だったっけ」
「このMacには、どのアプリが古いまま入っているんだろう」
と、1本ずつ確かめてはコピーする作業は、地味に面倒です。
最新版のビルドは、Dropboxの「Latest Builds」フォルダーにまとめて置いてあります。そこで、このフォルダーをもとに /Applications へまとめて入れる仕組みを作りました。
アプリにするか、.command にするか
最初は「これもアプリにしたほうがいいのかな」と考えました。ですが、今回は .command ファイル(ダブルクリックで動くシェルスクリプト)にしました。
Latest Builds に置いておけば、Dropboxでほかのcに同期される
Finderでダブルクリックするだけで動く
テキストファイルなので、直すときにビルドや署名がいらない
アプリにすると、この「入れるための道具」自体のビルド・署名・配布が必要になります。そうなると手間を減らしたいのに、かえって増えてしまいます。
すでにある常駐アプリ(ショートカットでアプリを起動するランチャー)の機能として組み込む案も考えました。ただ、新しいMacではそのランチャー自体がまだ入っていません。それに、自分自身を入れ替えるのは難しいので、見送りました。
Install Latest Apps.command でできること
Latest Builds に置いた Install Latest Apps.command をダブルクリックすると、ターミナルが開いて次の処理をします。
Latest Builds の .app と /Applications に入っているものを比べる
まだ入っていないもの、Latest Builds の方が新しいものだけを一覧にする
一覧を見て y を押したら入れる(N なら何もしない)
実行すると、こんなふうに一覧が出ます。
次の 2 本を入れます:
更新 IdBackgroundOff.app 1.1.1 (16) → 1.1.1 (17)
更新 MarkdownKitchen.app 1.42.0 (98) → 1.43.0 (99)
実行しますか? [y/N]/Applications の方が新しいもの(そのMacでビルドしたばかりのものなど)は「スキップ」として表示し、上書きしません。
入れ替えで気をつけたこと
ただコピーするだけなら数行で済みます。ですが、実際に使うとなると、気をつけたいところがいくつかありました。
新しいかどうかはバージョンで比べる
ファイルの日付ではなく、アプリの Info.plist にあるバージョン(CFBundleShortVersionString)とビルド番号(CFBundleVersion)で比べています。Dropboxで同期すると日付が変わることがあるためです。
アプリは Bundle ID で探す
途中でアプリの名前を変えたものがあります(AppLauncher → DocklessLauncher など)。名前で探すと、古い名前のアプリが残ったまま新しいアプリが増えてしまいます。
そこで /Applications 側は Bundle ID で探し、古い名前で入っていれば置き換えます。
起動中のアプリは終了してから入れ替える
自作アプリの多くはメニューバーに常駐しています。起動したまま中身を入れ替えると、動作が不安定になります。
そこで、起動中のアプリは終了させてから入れ替え、終わったら起動し直します。
cp ではなく ditto でコピーする
ditto を使うと、署名や拡張属性(Finderタグなど)を保ったまま .app をコピーできます。いったん一時的な名前でコピーし、うまくいってから古いものと入れ替えるので、途中で失敗しても壊れたアプリが残りません。
Dropboxの「オンラインのみ」に備える
別のMacでは、Dropboxのファイルがまだダウンロードされていないことがあります。コピーの前に中身を一度読んで、手元にダウンロードさせています。
自作アプリを一覧できる swApps フォルダー
もうひとつ欲しかったのが、自作アプリだけを一覧できる場所です。
Setapp のアプリは /Applications/Setapp にまとまっています。最初は「自作アプリも同じように専用フォルダーへ移そうか」と考えました。
ですが、ビルド用のスクリプトやIllustratorのスクリプトには /Applications/〇〇.app と直接書いた箇所がたくさんあります。移すと、それを全部書き換えなくてはいけません。
スマートフォルダーは Path Finder で使えなかった
自作アプリには、すべてFinderタグ「swwwitch」を付けています。そこで、このタグで絞り込むスマートフォルダーを作りました。
Finderではうまく一覧できました。ですが、ふだん使っている Path Finder では表示できませんでした。
シンボリックリンクを並べたフォルダーにした
最終的に、/Applications/swApps というフォルダーを作り、自作アプリへのシンボリックリンクを並べることにしました。
アプリ本体は /Applications の直下のまま(パスを書き換えなくてよい)
普通のフォルダーなので、Finder でも Path Finder でもお気に入りに登録できる
ダブルクリックすれば、リンク先の本体が起動する
このリンクは Install Latest Apps.command が実行のたびに作り直します。アプリが増えても、自動的に追いつきます。
Spotlight で検索しても、リンクがアプリとして二重に出てくることはありませんでした。
そもそも .command とは
.command は、ダブルクリックするとターミナルで実行されるシェルスクリプトです。中身は普通のテキストで、拡張子を .command にすると、Finderが「ターミナルで開くもの」として扱います。
動かすには、実行権限が必要です。ターミナルで次のように付けます。
chmod +x "Install Latest Apps.command"Mac どうしなら、Dropbox はこの実行権限も同期してくれます。そのため、ほかのMacでもそのまま動きます。
なお、ブラウザーやメールで受け取った .command は、初回は「開けません」と止められます。その場合は、右クリック→[開く]で実行できます。
1行目の #!/bin/zsh
スクリプトの1行目には #!/bin/zsh と書いています。これは「このファイルは zsh で実行する」という指定です。
zsh(Z shell)は、ターミナルで打った命令を読んで実行する「シェル」のひとつです。macOS Catalina(2019年)からは、ターミナルの標準のシェルになっています。どのMacにも入っているので、追加でインストールするものはありません。
zsh と bash
それより前の macOS では、bash が標準のシェルでした。Linux では今も bash が標準のことが多いので、ネットで見かけるシェルスクリプトの多くは bash で書かれています。
macOS が zsh に切り替えたのは、ライセンスの事情によるものです。bash はバージョン 4 からライセンスが GPLv3 に変わりました。そのため macOS には、その前のバージョン 3.2(2007年ごろ)が入ったまま更新されていません。今も /bin/bash は入っていますが、古いままです。zsh は制約のゆるいライセンスなので、新しいバージョンが入っています。
書き方はほとんど同じで、bash 用のスクリプトの多くは zsh でもそのまま動きます。ただ、細かいところに異なる点があります。
配列の番号:bash は 0 から、zsh は 1 から数える
変数の展開:bash では、引用符で囲まない変数は空白で分かれる。zsh では分かれない
ファイル名の展開:*.app に当てはまるファイルが無いとき、bash は *.app という文字のまま残し、zsh はエラーにする
今回のスクリプトでは、zsh ならではの書き方をいくつか使っています。
${0:A:h}:スクリプト自身が置かれているフォルダーを取り出す(Latest Builds の場所がわかる)
setopt null_glob:当てはまるファイルが無いときに、エラーではなく「何も無い」として扱う
${(@s:.:)1}:1.10.0 のようなバージョンを . で分けて配列にする
そのため、bash "Install Latest Apps.command" のように bash で実行すると動きません。1行目の #!/bin/zshは、このためにも大事な1行です。
自分のターミナルでどちらが動いているかは、次のコマンドで確かめられます。
echo $SHELL/bin/zsh と出れば zsh です。古い Mac から移行してきた場合などは、/bin/bash のままのこともあります。
まとめ
自作アプリの最新版は、Dropbox の Latest Builds に置いておく
ほかのMacでは、Install Latest Apps.command をダブルクリックするだけ
新しいものだけを、起動中なら終了させて入れ替える
/Applications/swApps から、自作アプリだけを一覧できる
アプリにするほどでもない「ちょっとした道具」は、.command にしておくと手軽です。同じように自作のツールが増えてきた方の参考になればうれしいです。