rcmdnk's blog

One Thing Remains

Macではdefaults というコマンドを使ってコマンドラインからシステムの設定や アプリ毎の設定を確認したり変えたりすることが出来ます。

ただ、どの様な事が出来るのか、は色々な所で書かれてる物を断片的に集めるしか無く、 何が出来るのか知る術がなかなかよい方法がありません。

defaultsコマンドそのもので現在設定されている物は確認出来るので、 そこからコマンドに戻してあげればある程度使いたい設定項目が 分かるだろう、ということでそのリストを作るコマンドを作ってみました。

parse-plist

ということで作った物が以下にあります。

bin/parse-listを適当なPATHの通ったディレクトリに入れるか、 Homebrewで

$ brew install rcmdnk/rcmdnkpac/parse-plist

でも入れられます。

何が出来るかというと、defaultsコマンドの出力を解析して

defaults write "com.apple.dock" "orientation" -string "bottom"

みたいな実際にコマンドラインから実行できる形のコマンドリストを作ります。

以下、実際何をやってるかについてちょっと説明。

プロパティリスト

Macでは プロパティリスト(property list, plist) と呼ばれるファイルで 色々な設定項目を保存しています。

plistはいくつかのフォーマットで書くことが出来て、 現在はほとんどがバイナリーファイルかXMLファイルになっています。

/Library/Preferences/User/$USER/Library/Preferences 等に環境設定ファイル群としてのplistが見つけられます。

また、 /Applications/Firefox.app/Contents/Info.plist の様にアプリの中にもplistが入っています。 ただ、アプリの後付の設定は /Users/$USER/Library/Preferences/org.mozilla.firefox.plist みたいに別途用意されていることが殆どです。 (アプリ内の物は後から変更しない。)

これらの設定を一通り見たい場合にはdefaults readコマンドを使うと

$ defaults read
{
    "Apple Global Domain" =     {
        AppleAntiAliasingThreshold = 4;
        AppleAquaColorVariant = 6;
        AppleEnableSwipeNavigateWithScrolls = 1;
        AppleHighlightColor = "0.500000 0.500000 0.500000";
        AppleInterfaceStyle = Dark;
        AppleKeyboardUIMode = 1;
        AppleLanguages =         (
            ja,
            en
        );
        AppleLocale = "ja_JP";
....

と言った具合に NEXTSTEP と呼ばれるフォーマットで出力されます。

この時、表示されるのは /Library/Preferences/User/$USER/Library/Preferencesの下にあるもの全て、 です。(一応多分。)

/Applications/Firefox.app/Contents/Info.plistみたいな内容は表示されません。

また、必要な設定だけ欲しい時には欲しいドメインを指定します。

$ defaults read org.mozilla.firefox.plist
{
    NSNavPanelExpandedSizeForOpenMode = "{..., ...}";
    NSNavPanelExpandedSizeForSaveMode = "{..., ...}";
    NSNavPanelExpandedStateForSaveMode = 1;
    NSTreatUnknownArgumentsAsOpen = NO;
    PMPrintingExpandedStateForPrint2 = 1;
}

の様に一部だけ表示する事が出来ます。 ドメインdefaultsコマンドで設定を書くときとかにも最初に与える物で、 /Library/Preferences/User/$USER/Library/Preferences等の 下にあるplistのファイル名そのものです。

この表示されるJSONみたいなフォーマットは元々plistにも使われてたものですが、 OS X 10.0からこのフォーマットをplistで使うことは非推奨になっています。 ただ、人間には読みやすいフォーマットなのでdefaults read のデフォルト出力形式となっているようです。

最初このフォーマットがJSON的な感じだから簡単に扱いやすいだろう、 と思って適当に解析してみようと思いましたが、 以外とこのフォーマットを解析するのが面倒で途中で辞めました。

defaultsコマンドにはreadの他に、exportというサブコマンドもあって、 これを使うとNEXTSTEP以外のフォーマットで設定を書き出すことが出来ます。

exportを使うときには必ず出力先を指定する必要があって、

$ defaults export org.mozilla.firefox.plist ./test.plist

の様に通常ファイルを指定するとバイナリのplistが書き出されます。

一方、-を指定すると標準出力にXML形式で表示します。

$ defaults export org.mozilla.firefox.plist -
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>NSNavLastRootDirectory</key>
        <string>~/Dropbox/04_Physics/21_DoubleChooz/DCJMeeting</string>
        <key>NSNavPanelExpandedSizeForOpenMode</key>
        <string>{..., ...}</string>
        <key>NSNavPanelExpandedSizeForSaveMode</key>
        <string>{..., ...}</string>
        <key>NSNavPanelExpandedStateForSaveMode</key>
        <true/>
        <key>NSTreatUnknownArgumentsAsOpen</key>
        <string>NO</string>
        <key>PMPrintingExpandedStateForPrint2</key>
        <true/>
</dict>
</plist>

plistを扱いやすいものに変換する方法は色々あるようですが 1、 XML形式に関しては色々と解析するツールもあるので、こちらを使うことにしました。

plistの解析方法

plistをXMLにすると、XMLの要素としては string, real, integer, true, false, data, date, array, dict が含まれています。

取り敢えずPythonで色々扱いたいということで、 扱いやすいJSON型に変更したいと思います。

ただし、XMLの要素になってるもののうち、date, dataはJSONで対応するものが無いため、 JSON形式に変換するときに問題が起こります。

実際、defaults以外にもいくつかMacでplist関連のコマンドがあって、 plutilというコマンドがあるのですが、 このコマンドはJSON型にplistを変更することができるものの、 data, data型が混じってると失敗します。

$ plutil -convert json <INPUT> -o <OUTPU>

plutilはこんな感じで<INPUT>-convertで指定したフォーマットで<OUTPUT> に書き出せます(<INPUT><OUTPUT>それぞれ-にすることで標準入力、標準出力になります)。 <INPUT>の形式はplistのものならなんでもOK。 (バイナリ、XML、JSON等。)

ただ、plistの中にdatedateがあると

$ defaults export com.apple.dock.plist -|plutil -convert json - -o -
<stdin>: invalid object in plist for destination format

こんな感じでJSONにしようとするとこけてしまいます。

そこで、一旦XMLを自前で解析してみることにしました。

xml.dom.minidomを使った解析

XMLをminidomを使って解析します。

最終的にJSON形式とかにしたいのですが、 dateとかはこの時点でどうするか難しいんですが、 ここでは取り敢えず文字列にして、最初にdate:等と付け加えて保存しておくことにします。

実際にやってみたのがこんなコード。

parse-plist/parse-plist at check-node · rcmdnk/parse-plist

主にcheck_nodeの部分でXMLの各要素が何かを見て JSONのリストに埋めていっています。

それを最終的にdefaultsコマンドに書き換えていますが、 datedateの部分は変換時にきちんと治せてる保証はありません。

それから結構面倒な事をしてますが、結局のところ、 dateとかの内容をそのまま文字列にして頭にdate:とか付けてるだけなので、 だったらXMLの状態で要素名を変えてしまってから変更すれば良いのでは、ということで次。

plutilを使う

parse-plist/parse-plist

現状バージョンの方ですが、こちらでは 最初defaultsコマンドでXMLにして書きだした後、 plutilでJSONに変換しています。

この際、XMLの中のdatedata要素はstringに書き換え、 加えてその中身をdate:みたいに書き換えています。

これでplutilに与えてもエラー無く変換できるようになります。

XML出力しないでバイナリをplutilに与えることも出来ますが、 上の様な変換が必要なので一旦XMLにする過程を踏む必要があります。

また、一部日本語とかの取扱で、

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

みたいなエラーが出ることがあるので、 printする前に

import codecs
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

としてstdoutcodecsでラップしてあります 2

使い方

取り敢えず現状ある設定をするコマンドを知りたければ

$ parse-plist
defaults write "com.adobe.air.ApplicationInstaller" "NSNavLastRootDirectory" -string "~/Applications"
defaults write "com.adobe.air.ApplicationInstaller" "NSNavPanelExpandedSizeForOpenMode" -string "{1680, 1023}"
defaults write "com.apple.ActivityMonitor" "OpenMainWindow" -bool False
defaults write "com.apple.ActivityMonitor" "ShowCategory" -int 100
...

こんな感じでparse-plistを行えばdefaults readで見れる様な設定の defaultsコマンドが見れます。

一方、一部のplistだけを表示したい場合は

$ parse-plist com.apple.dock.plist
defaults write "com.apple.dock" "orientation" -string "bottom"
defaults write "com.apple.dock" "showMissionControlGestureEnabled" -bool True
defaults write "com.apple.dock" "autohide" -bool True
...

の様にドメインを与えればOK。この場合、拡張子のplistを省略しても構いません。 また、ファイル等をパスを含めて直接指定することも出来ます。

ただし、設定がdatedata、もしくは配列や辞書を中に持つものはきちんと コマンドとして成立してない部分があります。 従って、これらのものはデフォルトでは無視して出力しないようにしています。

これらも含めて出力するにはparse-plist --dateのように個々の出力を指定するか、 parse-plist --allとして全て出力するようには一応出来ます。

取り敢えずこんな感じでdefaultsコマンドの一覧が見れるようになるよ、というもの。

もうちょっと色々やってみようというところ

既に上のスクリプトに入ってますが

$ parse-plist --user

とすると、 /User/$USER/Library/Preferences を一旦対比させてparse-plistを実行し、さらに戻してからもう一回実行し、 差分を表示させる事が出来ます。

これによって、自分が変更した部分を調べる、ということがしたかったんですが、 実はデフォルトでも /User/$USER/Library/Preferences の中に書かれているものは沢山あって、これらが全て出力されてしまいます。

なので、自分が変更したものだけ、ではなくて、単に /User/$USER/Library/Preferences で設定されてるもの全て、が得られるだけです。

同夜に

$ parse-plist --system

をすれば /Library/Preferences 無しの状態とありの状態で比べる事になりますが、これも単にここにあるものをリストするだけ。

なので本来欲しかったものとは違います。

ユーザー領域の方に関しては、新たなユーザーを作ってそこで出来た マッサラなPreferencesを取ってくることでそれに一時的に置き換えてチェック、 みたいなことはしようと思えば出来て、ちょっと手動でやってみましたが、 まあまあ、欲しいものが得られました。

ただ、/Library以下のものは初期設定を得る方法が無いので、 (再インストールすれば良いわけですが。。。) どうしようもないな、と。

いずれにしろ、簡単に初期状態との比較を取ることが思いつきません。

ユーザーを初期化したら出来るPreferencesがあるので、 どこかには情報があるわけで、どうにかすれば取れるんでしょうが、 良く分かりません。

ということで、取り敢えず現状、単に今あるplistdefaultsコマンドに なんとなく変更できるようになった、と言うだけの状態です。

勿論、この状態だと、初期で使われていない値で自分も使ってないものは見れません。 (むしろそれを知る事が一番やりたいことだったり。。。)

1つ事としては、まず1回コマンドリストを書き出しておいて、 環境設定やその他環境設定が出来るアプリなんかで設定を変えた後、 もう一度リストを書き出し、 そのdiffを取って変更点を見ることで環境設定などの変更を defaultsコマンドの形で知ることが出来る、ということくらいです。

取り敢えず今のところあまり使い物にならないですが、 今後のためのメモとして残しておきます。

Sponsored Links
Sponsored Links

« Python編集環境を整える: pytestとかvimのプラグインとか Brewfileはオワコンではない »

}