いい加減 JapaneseIM (macOS Sierra 編)

このブログで過去にも度々触れてきてますが,またコイツの話.

cp: /System/Library/Input Methods/JapaneseIM.app/Contents/Resources/KeySetting_Default.plist: No such file or directory

げんなり.
気分転換に外の空気に触れようと,ちょっとふらりふらり彷徨っていたら.;

El Capitan では「/System/Library/Input Methods/JapaneseIM.app/Contents/Resources」でしたが。
Sierra では「/System/Library/Input Methods/JapaneseIM.app/Contents/PlugIns/JapaneseIM.appex/Contents/Resources」となっています。

は(呆.

命題:「スペースおよび記号は,常時半角で入力するよう,シェルスクリプト使って設定する.Sierra でもな!」の巻

以前やりました,こちらの Sierra 版ってやつ,か.

High Sierra のリリースがアナウンスされてるってのにね

f:id:wooweezoowee:20170712183829j:plain

photo by "fancy crave on unsplash"  fancy crave

今更な

以前より用意していたスクリプトを引っ張り出し,幸い,KeySetting_Default.plist のパスを更新すれば,そのままで動いてくれたので一安心.

改訂.

#!/bin/bash -eu


#
# PlistBuddy のパスを獲っておく
#
# 期待される値:
# pb=/usr/libexec/PlistBuddy

pb=$(mdfind -onlyin /usr/libexec -name Plistbuddy)


#
#  KeySetting_Default.plist のパスを獲る
#
# 期待される値:
# plistis=/System/Library/Input Methods/JapaneseIM.app/Contents/PlugIns/JapaneseIM.appex/Contents/Resources/KeySetting_Default.plist

echo "'locate' コマンドの準備 1/2; データベースの生成..."
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist &
wait
echo "...終了."

echo "'locate' コマンドの準備 2/2; データベースを更新..."
sudo /usr/libexec/locate.updatedb &
wait
echo "...終了."

plistis=$(sudo locate  "KeySetting_Default.plist")
if [ -z "${plistis}" ]; then
  echo "'KeySetting_Default.plist' が見つかりませんでした.処理を中断しました."
  exit 1
fi

function keyCheck {

  val=$("${pb}" -c "Print :keys:${1}:${2}:character" "${plistis}" 2>/dev/null || true)

  if [ "${val}" = "" ]; then
    sudo "${pb}" -c "Add :keys:${1}:${2}:character string ${3}" "${plistis}"
  else
    sudo "${pb}" -c "Set :keys:${1}:${2}:character ${3}" "${plistis}"
  fi
}

# plist のバックアップ
[ ! -e ${HOME}/temp ] && mkdir ${HOME}/temp
sudo cp -f "${plistis}" "${HOME}/temp/KeySetting_Default.plist~$(date '+%Y%m%d%H%M')"

sudo "${pb}" -c "Set :keys:before_typing:\'' \'':character ' '" "${plistis}"  #     space
sudo "${pb}" -c "Set :keys:*:\''|\'':character '|'" "${plistis}"              # |   vertical bar
sudo "${pb}" -c "Set :keys:*:\''\!\'':character '\!'" "${plistis}"            # !   exclamation
sudo "${pb}" -c "Set :keys:*:\''"'\"'"\'':character '"'\"'"'" "${plistis}"    # ”   double quotation
sudo "${pb}" -c "Set :keys:*:\''#\'':character '#'" "${plistis}"              # #   sharp
sudo "${pb}" -c "Set :keys:*:\''$\'':character '$'" "${plistis}"              # $   dollar
sudo "${pb}" -c "Set :keys:*:\''%\'':character '%'" "${plistis}"              # %   percent
sudo "${pb}" -c "Set :keys:*:\''&\'':character '&'" "${plistis}"              # &   ampersand
sudo "${pb}" -c "Set :keys:*:\''\'\'':character '\''" "${plistis}"            # ’   apostrophe(single quotation)
sudo "${pb}" -c "Set :keys:*:\''(\'':character '('" "${plistis}"              # () parentheses
sudo "${pb}" -c "Set :keys:*:\'')\'':character ')'" "${plistis}"
sudo "${pb}" -c "Set :keys:*:\''*\'':character '*'" "${plistis}"              # *   asterisk
sudo "${pb}" -c "Set :keys:*:\''+\'':character '+'" "${plistis}"              # +   plus
sudo "${pb}" -c "Set :keys:*:\''\:\'':character ':'" "${plistis}"             # :   colon
sudo "${pb}" -c "Set :keys:*:\'';\'':character ';'" "${plistis}"              # ;   semicolon
sudo "${pb}" -c "Set :keys:*:\''<\'':character '<'" "${plistis}"              # <> angle bracket
sudo "${pb}" -c "Set :keys:*:\''>\'':character '>'" "${plistis}"
sudo "${pb}" -c "Set :keys:*:\''=\'':character '='" "${plistis}"              # =   equals
sudo "${pb}" -c "Set :keys:*:\''?\'':character '?'" "${plistis}"              # ?   question
sudo "${pb}" -c "Set :keys:*:\''@\'':character '@'" "${plistis}"              # @   at
sudo "${pb}" -c "Set :keys:*:\''^\'':character '^'" "${plistis}"              # ^   caret
sudo "${pb}" -c "Set :keys:*:\''_\'':character '_'" "${plistis}"              # _   underscore
sudo "${pb}" -c "Set :keys:*:\''\`\'':character '\`'" "${plistis}"            # ‘   back quote
keyCheck "before_typing" "\''/\''" "'/'"                                      # / slash (solidus)
keyCheck "typing" "\''/\''" "'/'"
keyCheck "before_typing" "\''\\\\\''" "'\\\'"                                 # \ backslash (reverse solidus)
keyCheck "typing" "\''\\\\\''" "'\\\'"


sudo killall -HUP JapaneseIM

echo "*** おわり ***"

イメージとしてはこんなもん,程度に捉えてください.*1

そうそう,勿論 rootles モードが効いている状態では効きませんので.この意味が分からない方は,やらない方が良いでしょう.
あと,killall してますが,再起動の方が確実.

今回は,ちょいと思うところあって,「一応,KeySetting_Default.plist の内容はこんな感じになりますよ」と言うものを置いておきたいと思います.

今回の作業を通して知ったこと,分かったこと

これも以前より何となく気になっていたことがあって,せっかくの機会だと思い,その辺をハッキリしておこうと段階踏んで探る作業を行いました.

その備忘録.

Plist において,入力のキーを表すキャラクタは &apos; でクォートしているが,出力文字の方は要らない

これは見ての通り.

入力のキーを表す <key> タグにて定義するキャラクタは,シングルクォーテーション,しかも実態参照 &apos; で行う.
しかし一方,それによって出力する文字列を定義する側の character キーの <string> タグの方では,その必要はない.

具体的には,こう.
上は “スペースバーを入力したら,全角スペースを出力する.” という例.;

<key>&apos; &apos;</key>
<dict>
  <key>command</key>
  <string>direct_input</string>
  <key>character</key>
  <string> </string>
</dict>

入力の スペースの方は,&apos; &apos; とする必要があるが,
出力の全角スペース   は,囲む必要はない.

そのキャラクタを囲むのは,実態参照 &apos; じゃなくても良い

直接,シングルクォーテーション ' 使って囲んでも問題ないみたいです.

これはこちら Gist に置いておきましたスクリプトを走らせた後の内容を見て頂いても分かるでしょうか.

ここでも上述例を使って表すと,こう.;

<key>' '</key>
<dict>
  <key>command</key>
  <string>direct_input</string>
  <key>character</key>
  <string> </string>
</dict>

こんな “見れば分かる当たり前のこと” を,あえて確認したかったかと言うと,PlistBuddy コマンドのエントリー “entry” に,どう書いてやるのが正解なのか,と言うのが気になったのが切っ掛け.

「今まで直接 ' 使って囲んでいたけど,もしかして本当は &apos; の方が良かったり?」

OS 側がデフォルトとして提供するのが実態参照による書き方で,それがもし正解であるならば,その作法が従うべきだと思ったし,色々スッキリすると考えたのです.

そして結果,上述のとおり &apos; による縛りは特に無いようで,' で記述しても問題ない様子.

更に,PlistBuddy においてですと,"entry" で入力キーキャラクタ指定のクォートに実態参照 &apos; を使って書くと,上手く特定してくれない模様.
具体的に言うと,半角スペースを特定するのに,&apos; &apos; とすると上手くヒットせず,' ' といった書き方をしてやると上手くいくっぽい.それは Set だけでなく Plint においても.

もしかしたら自分のやり方,間違っているのかもしれませんが.

$ "${pb}" -c "Print ':keys:before_typing:\&apos; \&apos;'" "${plistis}"
Print: Entry, ":keys:before_typing:&apos; &apos;", Does Not Exist
$ "${pb}" -c "Print ':keys:before_typing:\\&apos; \\&apos;'" "${plistis}"
Print: Entry, ":keys:before_typing:&apos; &apos;", Does Not Exist
$ sudo "${pb}" -c "Set :keys:before_typing:'&apos; &apos;':character ' '" "${plistis}"
Password:
Set: Entry, ":keys:before_typing:&apos; &apos;:character", Does Not Exist

これらのことから,とりあえず自分の中で,囲みで使うシングルクォーテーションは,OS 提供時デフォルト状態の plist にあるよう,実態参照ではなく,素直に ' を使う方で良い,としておくことに.

PlistBuddySet するとき “entry” も “value” も,そのキャラクタの記述は実態参照でなくても良い

みたいです.

上述のスクリプトを参照.

PlistBuddy で value に <>,そして & と実際のキャラクタで定義してもキチンと処理は行い,設定もされる.そしてなおかつ,それら文字列は実態参照に変換したうえで書き込んでくれるっぽい.

心配しなくてよい.

PlistBuddySet で plist ファイルを更新すると,各要素の順序 (でいいのかな?) がぐちゃぐちゃになってくる

こちらの diff も合わせて参照して頂ければ伝わるかと思いますが.

これは「試しに.」と,まずはじめに “スペース” のみについて,PlistBuddy で設定して出来るかどうかを確認してみた中で気になったこと.

pb=/usr/libexec/PlistBuddy
plistis=/System/Library/Input Methods/JapaneseIM.app/Contents/PlugIns/JapaneseIM.appex/Contents/Resources/KeySetting_Default.plist

sudo "${pb}" -c "Set :keys:before_typing:\'' \'':character ' '" "${plistis}"

とスペースの入力のケース,1 つだけサンプリングし,上のようにターミナルで直接入力し,実行.

実行後,確認のため対象の KeySetting_Default.plist の中身を覗いてみたら,元の記述行にその内容がない.

揚句,更新前オリジナルの状態では,一番最初に記述されていた version の要素が,一番下に来ているとかって.

ん? 何か書き込むタイミングなどで法則があったりするのかな,と思って見てみたものの,わからず.

正直気持ち悪いですが,上手く動作しているみたいなので良しとします.受け流す 笑
何か解決方法あるのでしょうか.

と言ったところで,plist に関する話は終わりです.

keysetting_default.plistmdfind では探せない? そして locate を知って,それ使った

今回のように,keysetting_default.plist の場所移動によってスクリプトが機能しなくなるのは避けたいと考え,当該ファイルを探索する仕組みを取り入れようと考えました.

そんな中での話です.

ファイルの検索となれば,いつもの扱いにくい midfid コマンド,となるわけですが,今回は薄っすら感じていたのですが,心配していた通り上手く引っ掛けてきてくれない様子.

mdfind の振る舞いは,可視状態 invisible などの属性や,アクセス権等によって影響するものらしい話,聞いたこともあり,この keysetting_default.plist もそれ絡みの話なのかな,と*2ぼんやり想像していたりします.

mdfind seems to ignore a lot of hidden files. ..略.. I haven’t found any way to search for files in some hidden directories like /private/etc/.

おそらくこれだって必要な “おまじない” をかけてやれば,きちんと拾ってくれるのでしょうが,正直,今かったるい笑.

仕方なく「find かなぁ」と彷徨っていたら,こんなエントリに出会いました.;

» Mac/Linuxの「locate」コマンドで高速ファイル検索|「find」コマンドとの違いから「mdfind」の紹介まで - Qiita

これまでもしばしば目に入ってはきていたのですが,特に必然を感じなかったのと,何より敷居が高そうでスルーしてきたのですが,「mdfind だるい」となった今,その距離感が劇的(笑に変化し,早速使ってみることに.

具体的な使い方等は,先のエントリにあるとおりで OK でした.*3

結果,さらっと見つけてくれて,願った通りしっかりとパスを引っ張って来てくれました.素晴らしい.

The locate command is very useful if you’re looking to track down every instance of a file, filetype, app, extension, things hidden deep in system folders, or just about anything else that Spotlight can’t manage.

locate は,Spotlight が管理できないようなどんなファイルだって探すんだよ,って.

めでたしめでたし.

PlistBuddy は,type でも which でもわからない.

これで気を良くし,「PlistBuddy のパスも」と type コマンド使ってみたら,結果を返してきません.which もダメ.

/usr/libexecディレクトリは、初期設定ではパスが通っていないこともあり、 ..(略).. 「PlistBuddy」は、その/usr/libexecディレクトリに置かれているコマンドだ。

ということなだからなのでしょうか.

ずっと「OS 標準だから」という事で JapaneseIM の色々やり過ごしてきましたけど,今回の出来事を経ていい加減,自分の感情に正直になって拘るのやめようかなと本気で思ってきています.

あと,この作業で vim というツールを使ったことで色々助けられたのですけど,それについても触れられればと思ったのですが....また別の話とします.

はいおしまい.

*1:一応自分の環境では正しく動作して,設定も効いてくれてますけど.

*2:‘keysetting_default.plist’ のある場所は,'/System' 配下で,システム整合性保護 System Integrity Protection の対象ですし.

*3:ちょっと異なったのは,《データベース作成に時間がかかるので数分まつ.》って言っているけど,自分の場合はむしろ,その次の ‘sudo /usr/libexec/locate.updatedb’ の方が “数分” かかったりするくらい.