ラベル AppleScript の投稿を表示しています。 すべての投稿を表示
ラベル AppleScript の投稿を表示しています。 すべての投稿を表示

2011年12月22日木曜日

プログラムでふりがなをふる

日本語は不便なもので、漢字かな交じり文を辞書順にソートするためには、ふりがなのデータが必要です。シラバスのデータを授業名順にソートしたり、「あかさたな」で分類した索引を作る必要が出てきたのですが、あいにく元データにはふりがながありませんでした。

日本語の文章を読み上げる際にも同様の問題が発生し、Mac OS X は標準で漢字仮名交じり文をひらがなだけに変換する機能を持っています(ことえりの一部)。授業名は人名と違ってそうそうひねくれた名前はないので、この機能を使えば普通に変換してくれるでしょう。どうしてもだめなものは、ことえりに単語登録してしまえばいいのです。

この機能を手軽に利用するには、コマンドラインアプリのSayKotoeriSayKotoeri2をインストールするとよさそうです。以下はコマンドラインから SayKotoeri を使った場合の処理結果です。

% /usr/local/bin/SayKotoeri -o '情報処理概論'
じょーほーしょりがいろん
% /usr/local/bin/SayKotoeri -om '情報処理概論'
じょうほうしょりがいろん

読み上げのためのシステムなので、情報は「じょーほー」になってしまいます。m オプションを同時に指定すると、純粋にふりがなになります。

シラバスのデータは、Excel で一覧になっていて、AppleScript で Excel のデータを取り出し、SayKotoeri でふりがなを取得した後、Excel のセルに書き戻すことにしました。プログラムを書いて、一括変換していたところ、なぜか異常終了してしまいます。

イベントログを追っていたところ、次のようなことが分かりました。「Ⅵ」単体では大丈夫ですが、任意の文字の後に「Ⅵ」が来ると異常終了します。

% /usr/local/bin/SayKotoeri -om 'Ⅵ'
Ⅵ
% /usr/local/bin/SayKotoeri -om 'あⅥ'
LATextToMorphemes error
analysis = nil

「髙」(はしごだか)でも同様の現象が起こります。

% /usr/local/bin/SayKotoeri -om '髙'
髙
% /usr/local/bin/SayKotoeri -om 'あ髙'
LATextToMorphemes error
analysis = nil

「①」は、異常終了こそしませんが、どこかで見たような文字化け後の読み方になっています。

% /usr/local/bin/SayKotoeri -om '①'
かっこにち

どうやら、ことえり内部の文字コードの扱いに問題があるように思います(使用環境は 、Mac OS X 10.7.2 で、コマンドラインで使用している文字コードは UTF-8 です)。今は急いでいるので、正常に変換できない文字はなかったことにして、一旦ふりがなをふってしまいました。早く直ってほしいです。

2010年10月15日金曜日

iPhoto の写真にジオタグを設定するスクリプト

iPhoto '09 は写真に埋め込まれたジオタグで地図上に撮影地をプロットできます。とても面白い機能なのですが、私の Canon EOS KISS X2 には GPS なんて便利なものは付いてないので、何とかして手動で付けてあげないといけません。Google マップで撮影地を指定して、その場所に設定できたら便利だなーってことで、調べていたら、いとも簡単にできました。

こちらからダウンロードできます
iPhoto_set_geotag_to_photos.zip (3k bytes)

“iPhoto で選択中の写真の撮影地を設定” というスクリプトが入っています。使い方は以下のとおりです。

  1. iPhoto でジオタグを付けたい写真を選択します。複数選択してもかまいません。
  2. Safari で Google マップを開き、撮影地として指定したい場所を真ん中に持ってきます。ダブルクリックするとよいでしょう。
  3. この状態で、ダウンロードしたスクリプトを動かします。特に反応はありませんが、撮影地が設定されています。

※ Safari で複数のタブで Google マップを開いていると正しく動作しません。

スクリプトは、$HOME/Library/Scripts/Applications/Safari に入れて、スクリプトメニューから呼び出すと便利です。

配布には Dropbox を使っています。まだご利用でない方は、こちらから登録してください (無料、2GB + 250MB のボーナスディスクスペースがもらえます。) 。

2010年1月18日月曜日

Automator 雑感

サービスの作成プリントプラグインの作成ワークフローの作成、そしてGUI 付きのオリジナルのアクションの作成と、Automator を一通り使ってみました。

Automator が登場した当初、GUI で操作できるのは良いのですが「どうせおもちゃしか作れないんでしょ?」「オリジナルのアクションが作れるかもしれないけど、本当にしたいことは、Objective-C で書かないといけないんでしょ?」と思っていました。これは誤りでした。一通り使ってみた現在では、生産効率の高いソフトウェアプラットホームとして、十分使えるというのが認識です。「AppleScript を実行」や「シェルスクリプトを実行」で使い捨てで書けますし、必要に応じて GUI 付きのアクションに仕立てて、再利用することもできます。

これからも積極的に Automator を使っていきますよー!

2010年1月17日日曜日

サブフォルダを再帰的に辿って、ファイルにナントカする

指定したフォルダに含まれるすべてのファイル (サブフォルダも含む) に対して、何かを行うというのはよくありますが、AppleScript で書くと、意外と面倒だったりします。

Automator なら一発です。

最初に指定するテンプレートは、「ワークフロー」です。ワークフローは、Automator を起動して、実行ボタンを押して動かします。

空のワークフローに、以下のアクションを追加します。

  • 「指定された Finder 項目を取得」アクションは、選択している GUI でファイルやフォルダを選択し、そのリストを次のアクションへ渡します。上の例では、2つのフォルダを選択しています。※
  • 「フォルダの内容を取得」アクションは、受け取ったフォルダに含まれるファイルやフォルダの一覧を取得します。ここでは、サブフォルダに含まれるファイルやフォルダも再帰的に取得してほしいので、「見つかったサブフォルダからも取得」にチェックを入れておきます。
  • 「Finder 項目にフィルタを適用」アクションは、受け取ったファイルやフォルダの一覧を、指定した条件でフィルタリングします。上の例では、拡張子とファイル名の 2つの条件を追加しています。「結果」ボタンを押して、何がヒットしたかを表示させています。4つのファイルのリストが次のアクションに渡されます。
  • 最後に AppleScript で実際に何をするかを書きます。input には、ファイルのリストが渡されます。parameter は、とりあえず無視で OK です。もし、この後、別のアクションを実行したいなら、run ハンドラで次のアクションに渡すデータ (ファイルのリストや、文字列) を返却してください。

AppleScript が使える人は、今日から Automator で幸せになれますよ。シェルスクリプト、Perl、Ruby、Python でも同様に書けます。

※ サービスやアプリケーション (ドロップレット) の形で作ると、ユーザによって起動時にフォルダが指定されますので、この部分は不要になります。

2009年8月30日日曜日

ここが非互換 Mac OS X 10.6 -- iCal

Mac OS X 10.6 Snow leopard が発売になりました。

当サイトでは、よく iCal 関係の AppleScript を扱っていますが、Mac OS X 10.6 では、少し挙動が変わりました。試しに、iCal でカレンダー「テスト」に「テスト」というイベントを作って、AppleScript から観察してみます。

tell application "iCal"
  tell calendar "テスト"
    properties of event 1
  end tell
end tell

以下は 10.5.8 (iCal 3.0.8) の結果です。

{
  allday event:false,
  class:event,
  description:missing value,
  end date:date "2009年 9月 2日 水曜日 1:00:00 PM",
  excluded dates:{},
  id:"C0DC5F01-4183-412E-95AB-9414FD8111C6",
  location:missing value,
  recurrence:"",
  sequence:2,
  stamp date:date "2009年 8月 30日 日曜日 2:27:03 PM"
  start date:date "2009年 9月 2日 水曜日 10:00:00 AM",
  status:none,
  summary:"テスト",
  url:missing value,
}

以下は 10.6 (iCal 4.0) の結果です。

{
  allday event:false,
  class:event,
  description:missing value,
  end date:date "2009年9月2日水曜日 16:00:00",
  excluded dates:{},
  id:"6A5888C1-D872-4C5F-B053-09CD8FC5067D"
  location:missing value,
  recurrence:missing value,
  sequence:2,
  stamp date:date "2009年8月30日日曜日 14:20:48",
  start date:date "2009年9月2日水曜日 13:00:00",
  status:none,
  summary:"テスト",
  url:missing value,
}

プロパティリストの結果は、比較しやすいように辞書順に並べ替えています。以上のように、recurrence に値が設定されていない時の結果が違います。description に "" (空文字列) を設定した時の挙動も違い、iCal 3.0.8 では "" になりますが、iCal 4.0 では missing value になります。

なお、missing value を文字列に変換して扱ってはいけません。AppleScript では missing value as string は "missing value" という文字列になるというよく分からない仕様なので、気をつけましょう。

2009年5月10日日曜日

Bento 2.0.4 リリース…だが…

Mac 専用パーソナルデータベースソフト Bento 2 のバージョン 2.0.4 へのアップデータが公開されました。 iPhone / iPod Touch 版の Bento も公開され、これとのデータ同期と、Bento 2 のパフォーマンスの改善が主な改良点です。

毎度期待している AppleScript のサポート改善は、今回もありませんでした。

確認されている不具合

過去のバージョン (2.0.3 以前) には、希に iCal イベントに重複レコードが発生するというバグがありました。重複レコードが発生しているデータベースを Bento 2.0.4 に読み込ませた場合、Bento 2 を起動する度に、以下の現象が発生します。

  • エラーが発生したので終了する、というダイアログが出るが、実際には終了しない。
  • すべての iCal イベントが重複レコードとしてコピーされる。

起動する度に、iCal イベントが増えていくので、たまったものではありません。なお、重複レコードが発生した場合、Bento から削除することはできません。同様の現象は、アドレスブックでも発生するという報告もあるようです。

一時的な解決策

Bento 2 の新しいアップデータが出るのを待つのが最良の選択です。が、それまでの間、Bento 2 が使えないのは困るという人向けに、Bento 2 のデータベースを直接操作して、重複レコードを削除するスクリプトを公開します。

以下の点を理解し、同意いただける方のみご利用ください。

  • Bento 2.0.4 専用です。
  • データ破壊、データ消失の可能性がありますので、必ずバックアップを取ってから実行してください。清野は、本スクリプトを使用した結果について一切の責任を免れるものとします。
  • 本スクリプトは iCal イベントの重複レコードを削除します。
  • 重複レコードのうち、最も古い (最初に追加された) レコードを残します。そうでないレコードが関連レコードとして参照されている場合、データベースに不整合が発生します。

私の環境では、このスクリプトにより不具合は解消され、正常に使えるようになっています。

-- Delete duplicated events from BentoDB.
-- Use this script AT YOUR OWN RISK.

property sqlite3 : "/usr/bin/sqlite3"
property bentodb : "/Users/your-account/Library/Application Support/Bento/bento.bentodb/Contents/Resources/Database"

on run
 set lastUUID to ""
 repeat with e in my getEvents()
  set the AppleScript's text item delimiters to "|"
  set pk to item 1 of (text items of e)
  set uuid to item 2 of (text items of e)
  if lastUUID is uuid then
   my delEvents(pk)
  else
   set lastUUID to uuid
  end if
 end repeat
end run

on getEvents()
 set the AppleScript's text item delimiters to ASCII character of 13
 text items of my doSQL("SELECT gnpk, gn_gnExternalUID FROM gn_lib_event ORDER BY gn_gnExternalUID ASC")
end getEvents

on delEvents(pk)
 my doSQL("DELETE FROM gn_lib_event WHERE gnpk = '" & pk & "'")
end delEvents

on doSQL(sql)
 do shell script sqlite3 & " '" & bentodb & "' '" & sql & "'"
end doSQL

2009年5月1日金曜日

iCal: event オブジェクトの location プロパティ

iCal の event オブジェクトの location プロパティは、ちょっとクセがあります。

場所が指定されていないとき、location プロパティの値を変数に代入しても、その変数の値は未定義となります。変数に触ったとたんにエラーになるので、以下のように、ヌル文字列に正規化して使うのがよさそうです。

 set loc to location of theEvent
 try
  loc
 on error
  set loc to ""
 end try

知らないとハマる箇所です。その他のプロパティには、こんな変なクセはないんですけどね…。

2009年4月30日木曜日

第○何曜日を計算する

PHPで書かれていたものの移植です。y年m月の第nd曜日の date オブジェクトを返します。

-- y年m月の第nd曜日の date オブジェクトを返す。
on getWhatDayOfWeek(y, m, n, d)
 -- 指定した年月の1日の曜日を取得
 set f to current date
 set year of f to y
 set month of f to m
 set day of f to 1
 set dd to d - (weekday of f) + 1
 -- 1週間を足す
 if dd ≤ 0 then
  set dd to dd + 7
 end if
 set day of f to dd
 -- 第n曜日まで1週間を足し込み
 set day of f to (day of f) + (n - 1) * 7
 return f
end getWhatDayOfWeek

。例えば、2009年 4月の第二水曜日を得る場合は、以下のように使います。

my getWhatDayOfWeek(2009, 4, 2, Wednesday)

すると、以下のような date オブジェクトが返ります。時刻は無視してください。

date "2009年 4月 8日 水曜日 10:44:00 AM"

なお、valid でない日付を指定した場合でもそれなりに動いてしまいます。例えば、2009年 13月は2010年 1月と読み替えられます。また、2009年 4月の第5金曜日は 2009年 5月 1日が返ります。意図した結果となっているかどうかは、呼び出す側で確認してください。

2009年3月14日土曜日

date オブジェクト

date オブジェクトのリファレンスがどこにもないので、判明している点についてのメモ書きです。

date オブジェクトの作成方法

make new date with properties { ... } でうまく作成できません。

次のように作成します。

set s to "2009-03-14 13:05:49"
date s -- should be date "2009年 3月 14日 土曜日 1:05:49 PM"

日付だけでも OK

set s to "2009-03-14"
date s -- should be date "2009年 3月 14日 土曜日 0:00:00 AM"

プロパティ

判明しているプロパティは以下の通り。時・分・秒は取れないとしている資料がありますが、現行バージョンでは取れます。

year of (current date) -- 年
month of (current date) -- 月
month of (current date) as integer -- 月 (数字)
day of (current date) -- 日
weekday of (current date) -- 曜日
weekday of (current date) as integer
      -- 曜日 (数字: 1. Sunday .. 7. Saturday)
hours of (current date) -- 時
minutes of (current date) -- 分
seconds of (current date) -- 秒
time of (current date) -- 0:00:00 AM からの経過秒数

month of は数字を使って書き換えても大丈夫。

set d to date "2009年 3月 1日 日曜日 0:00:00 AM"
set month of d to 4
d -- should be date "2009年 4月 1日 水曜日 0:00:00 AM"

日付に関する計算

date オブジェクトに対して、秒単位で加減算をすることで、n 秒前、n 秒後の日付を得ることができます。

set d1 to date "2008年 2月 29日 金曜日 0:00:00 AM"
set d1 to d1 + 60 * 60 * 24 * 10 -- 10日後
d1 -- should be date "2008年 3月 11日 火曜日 0:00:00 AM"

set d2 to date "2008年 2月 29日 金曜日 0:00:00 AM"
set d2 to d2 - 60 * 60 * 24 * 10 -- 10日前
d2 -- should be date "2008年 2月 19日 火曜日 0:00:00 AM"

日の加算はうまく行くようです。n日後とかを計算するのに便利です。

set d to date "2008年 2月 29日 金曜日 0:00:00 AM"
set day of d to (day of d) + 1 -- 1日後
d -- should be date "2008年 3月 1日 土曜日 0:00:00 AM"

day of に 32日 を設定しても大丈夫です。

set d to date "2008年 3月 31日 月曜日 0:00:00 AM"
set day of d to (day of d) + 1 -- 1日後
d -- should be date "2008年 4月 1日 火曜日 0:00:00 AM"

day of に 0 や マイナスの数値を設定するとエラーになります。n 日前を指定する時は、前述の秒単位の減算を使います。

set d to date "2009年 3月 1日 日曜日 0:00:00 AM"
set day of d to (day of d) - 1 -- 1日前

時間に関する計算

日付のように、各プロパティに関する設定はできないので、time of で計算します。

set d to date "2009年 3月 1日 日曜日 0:00:00 AM"
set time of d to (time of d) + 3600 -- 3600秒 (1時間後)
d -- shold be date "2009年 3月 1日 日曜日 1:00:00 AM"

オーバーフローしても大丈夫。

set d to date "2009年 3月 1日 日曜日 11:53:00 PM"
set time of d to (time of d) + 3600 -- 3600秒 (1時間後)
d -- shold be date "2009年 3月 2日 月曜日 0:53:00 AM"

time of に 0 や マイナスの数値を設定するとエラーになります。n 秒前を指定する時は、前述の秒単位の減算を使います。

set d to date "2009年 3月 1日 日曜日 0:00:00 AM"
set time of d to (time of d) - 1

日付の比較

日付はちゃんと比較できます。

set d1 to date "2009年 3月 14日 土曜日 0:00:00 AM"
set d2 to date "2008年 3月 14日 金曜日 0:00:00 AM"
d1 > d2

2009年2月28日土曜日

Web ページ巡回スクリプト (更新チェック付き)

AppleScript で書いた Web ページ巡回スクリプトです。毎日、指定曜日、特定日に巡回する URL をスケジュールできます。簡単ですが、更新チェックの機能があります。ブラウザで一気にどばっと開きますので、たくさん URL を指定すると、大変なことになります。うまく曜日で散らしてください。

巡回 URL リストの作り方

巡回リストは、iCal の “巡回” という名前のカレンダーに保存します。まずは、iCal に “巡回” という名前のカレンダーを作ってください。

巡回したい Web ページごとに、終日イベントを作成し、URL を、イベントの URL 欄に記入します。イベントの繰り返しは、毎日、毎週 (複数の曜日を選ばないでください) を指定できます。繰り返しイベントの終了には対応していません (※) 。

※ ここら辺の iCal のサポートがとても poor なのです…。

更新チェックは、イベントごとに指定できます。チェックをしたい場合は、イベントのメモ欄に“/bin/cat”と入力してください。HTML のハッシュ値を保存し、前回の訪問時とハッシュ値が異なるときのみ、Web ブラウザで表示されます。カンのいい方はお分かりでしょうが、この箇所は、UNIX 流のフィルタになっています (※) 。ハッシュ値の計算対象を HTML 全体としたくない場合は、別のフィルタを実行させることができます。更新チェックのデータは、~/Library/Preferences/patrol script.plist に保存されます。

※ /usr/bin/curl -s URL | 指定されたフィルタ | /sbin/md5 を実行しています。

以下は、毎日巡回する Web ページを指定する例です。

以下は、毎週土曜日に巡回する Web ページを指定する例です。加えて、更新チェックを行うことが指定されています。

コード

コードの掲載をやめ、スクリプトを配布することにしました。

patrol web pages.zip (12k)

できれば、コメントなどをいただけるとうれしいです。

スクリプトは、現状のまま提供され、使用した結果の責任については、すべて使用者の責任とします。再配布はご遠慮ください。

清野の使い方

私は、毎朝一回、これを動かして、コーヒーを入れに行きます。コーヒーを入れて戻ってくると、全部のページのロードが完了している、という感じで使っています。

Safari の環境設定の「一般」の一番下に「アプリケーションでリンクを開く場合」という設定があります。これを「既存のウィンドウの新規タブに開く」にしておく方がよいと思います。タブにしておかないと、画面がウィンドウだらけになります…。

2009年2月26日木曜日

フォルダアクションを作るとき

フォルダアクションのスクリプトハンドラ adding folder items は、.DS_Store や Icon^M のような、Finder が自動的に作成するファイルの存在を考慮に入れて作らないといけない。

除外するファイルを考慮に入れた adding folder items ハンドラのサンプル。

-- 今日の日付のサブフォルダを作って、追加された各項目をそこに入れる

-- フォルダアクション (項目追加時) のハンドラ
on adding folder items to this_folder after receiving these_items
  -- フォルダアクションで操作対象外とするファイル
  set excludes to {".DS_Store", ".localized", "Icon" & (ASCII character of 13)}
  set results to my preparation(this_folder, these_items)
  try
    -- メインループ
    repeat with this_item in these_items
      tell application "Finder"
        -- 処理対象外のファイルは、ハンドラを invoke しない
        if name of this_item is not in excludes then
          my itemHandler(this_folder, this_item, results)
        end if
      end tell
    end repeat
  on error error_message number error_number
    display dialog error_message
  end try
end adding folder items to

-- 準備
on preparation(this_folder, these_items)
  -- ファイル移動のための準備
  set today_foldername to my getCurrentDate()
  tell application "Finder"
    -- 今日の日付のフォルダがない時は作る
    if not (exists folder today_foldername of this_folder) then
      make new folder at this_folder with properties {name:today_foldername}
    end if
    folder today_foldername of this_folder
  end tell
end preparation

-- 各項目に対する処理
on itemHandler(this_folder, this_item, target_folder)
  -- 追加された項目を今日の日付のフォルダに格納する
  tell application "Finder"
    considering application responses
      move this_item to the target_folder
    end considering
  end tell
end itemHandler

on getCurrentDate()
  -- yyyy-mm-dd のフォーマットで日付を取得する
  do shell script "date +%Y-%m-%d"
end getCurrentDate

2009年2月25日水曜日

iCal の終日イベント作成時のバグ

AppleScript から iCal の終日イベントを作成した時に、時間帯の扱いについてバグがある模様。iCal の環境設定の「時間帯サポートを有効にする」を変更しても、同じ結果となった。

実験環境

  • Mac OS X 10.5.6
  • iCal 3.0.6
  • スクリプトエディタ 2.2.1
  • AppleScript 2.0.1
  • 時間帯 Tokyo - Japan

テストコード

次のようなスクリプトを動かしてみると、(a) だけ昨日のイベントとなる。

tell application "iCal"
  tell calendar "テスト"
    set d to current date
    set hours of d to 8
    set minutes of d to 0
    copy d to d8  -- 今日の 08:00
    set hours of d to 9
    copy d to d9  -- 今日の 09:00
    set hours of d to 10
    copy d to d10  -- 今日の 10:00
  
    -- (a) 本日 8:00 - 9:00 の終日イベントを作る
    make new event at end with properties {summary:"テスト終日 (a)", start date:d8, end date:d9, allday event:true}
    -- (b) 本日 9:00 - 10:00 の終日イベントを作る
    make new event at end with properties {summary:"テスト終日 (b)", start date:d9, end date:d10, allday event:true}
    -- (c) 本日 8:00 - 9:00 の通常イベントを作る
    make new event at end with properties {summary:"テスト通常 (c)", start date:d8, end date:d9, allday event:false}
    -- (d) 本日 9:00 - 10:00 の通常イベントを作る
    make new event at end with properties {summary:"テスト通常 (d)", start date:d9, end date:d10, allday event:false}
  end tell
end tell

実行結果

この通り。多分、バグでしょう。

対策

今後、バグが直る可能性もあるので、対策としては、終日イベントを作るときは、開始時間を正午にしておく、ということでよさそうです…。

iCal 4.0.3 (Build 1388) では修正されていることを確認しました。(2010-09-05 追記)