Archive for 8月, 2011

2011/08/21 印刷可能なプリンタ一覧から選択してプリント実行 10.4〜10.7

印刷可能なプリンター一覧から選択してプリント実行するAppleScriptのMac OS X 10.7対応版です。

lpstatコマンドを呼び出して、その結果をparseしてプリンタ名を取り出す……という、あまり工夫もへったくれもない内容ではありますが、このlpstatコマンドが返す文字列が、OSのメジャーアップデートごとに微妙にフラフラと変わっているため、メジャーアップデートのたびに地道に調査して反映するという作業が必要になっています。

また、日本語以外のユーザー環境で利用する場合には、lpstatコマンドの返り値を調べて微調整する必要があります。Mac OS X 10.4の時には日本語環境でも英語メッセージが出ていたのですが、10.5以降は出力結果もローカライズされてしまって、本当にこれでいいのか疑問が残ります。

例によって、出力する紙のサイズを指定できないようになっています。

# 出力先のプリンタ名称取得ぐらいの機能は、標準で持っていてほしいところですが……

とりあえず、実際にMac OS X 10.7の稼働するMacBook Airで動作を確認しました。

スクリプト名:印刷可能なプリンタ一覧から選択してプリント実行 10.4_10.7
要・Mac OS X 10.4以上
set pList to getPrintCues() of me
set aPrinter to first item of (choose from list pList with prompt 出力先のプリンタを選択してください)

set aPrintSetting to {copies:1, starting page:1, ending page:1, target printer:aPrinter}

とりあえずSafariで印刷してみた。Safariである必要はない
tell application “Safari”
  activate
  
tell document 1
    try
      print with properties aPrintSetting with print dialog with」を「without」にすると、ダイアログなしで印刷
    on error
      display dialog プリント中になんかのエラーが発生しました。たぶん、キャンセルされたんでしょう buttons {“OK”} default button 1
    end try
  end tell
end tell

(*
プロパティ:

copies integer — プリントする書類の部数
collating boolean —
プリントの丁合をとるかどうか
starting page integer —
書類からプリントする最初のページ
ending page integer —
書類からプリントする最後のページ
pages across integer —
物理的なページ上に横方向に並べる論理ページの数
pages down integer —
物理的なページ上に縦方向に並べる論理ページの数
requested print time date —
デスクトッププリンタが書類をプリントするべき時間
error handling standard/summarized/detailed —
エラーの処理方法
fax number text —
書類の送信先ファックス番号
target printer text —
出力先のプリントキューの名前

用紙サイズを指定できないのはどういうことなのか?(汗) 用紙サイズ指定できなかったら意味ないやんけ!
*)

プリントキューの名称一覧を取得
–10.7用に書き換え
on getPrintCues()
  set P to paragraphs of (do shell script “lpstat -p”)
  
set pList to {}
  
  
set osVer to (system attribute “sys2″) as number
  
  
repeat with i in P
    set j to contents of i
    
    
if osVer = 7 then
      –10.7
      
set aPName to trimStrings(j, プリンタ, は待機中です。) of me
    else if osVer = 6 then
      –10.6
      
set aPName to trimStrings(j, プリンター, は待機中です。) of me
    else if osVer = 5 then
      –10.5
      
set aPName to trimStrings(j, プリンタ, は待機中です。) of me
    else if osVer = 4 then
      –10.4
      
set aPName to trimStrings(j, “printer “, ” is idle. enabled since “) of me
    end if
    
    
if aPName is not equal to “” then
      set the end of pList to aPName
    end if
  end repeat
  
  
return pList
end getPrintCues
任意の文字列から指定開始子、指定終了子でトリミングした文字列を取り出す
on trimStrings(aString, fromStr, endStr)
  set fromLen to length of fromStr
  
set eLen to length of endStr
  
  
set sPos to offset of fromStr in aString
  
if sPos = 0 then return “”
  
set body1 to text (sPos + fromLen) thru -1 of aString
  
set ePos to offset of endStr in body1
  
if ePos = 0 then return “”
  
set body2 to text 1 thru (ePos - 1) of body1
  
  
return body2
end trimStrings

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/08/21 特定の語句を含むMail.appのフォルダ(mailbox)を抽出してフルパスを文字列化

Mail.app上で特定のキーワードを含むフォルダを探し出して、そのパスを一覧表示するAppleScriptです。

Mail.app上で、フォルダを作ってメールの整理をしている場合に、ある特定のキーワードを含むフォルダがどこに存在しているかを検索したくなることがあります。しかし、Mail.appにそういう機能は存在していません。

acs0.jpg

なければ作るまでで……AppleScriptで作り出して、フルパス表示用に作っておいたサンプルと合わせて所要時間5分ぐらいでしょうか。

Mail.appではフォルダ(mailbox)をフィルタ参照で抽出できるので、思いっきり楽でした。自分のメイン環境ではフォルダが1万6000個ぐらい存在(自動で作成させているので)しているのですが、個数のカウントも1秒かからない程度(MacBook Pro 2010 Core i7 2.66GHz)でした。

実際、本Scriptでもフォルダ(mailbox)のフィルタ参照による抽出はすぐに終わるのですが、フルパスをテキスト化するのに処理時間のほとんどの時間がかかっています。

acs1.jpg

acs2.jpg

本Scriptを早急に作る必要が出てきたのは……メールの整理中に間違えて「Accessibility Dev」MLのフォルダを捕まえてどこかにドロップしてしまって、フォルダが多すぎてどこに落としたか分からなくなってしまったためです。マウスを使っている場合には起こらない悲劇、、、

スクリプト名:特定の語句を含むMail.appのフォルダ(mailbox)を抽出してフルパスを文字列化
set searchKeyword to “AppleScript”

tell application “Mail”
  –特定キーワードを名前に含むフォルダ(mailbox)を抽出
  
set allPath to every mailbox whose name contains searchKeyword
  
  
–与えられたフォルダ
  
set bList to {}
  
repeat with i in allPath
    set the end of bList to getFullPath(i) of me
  end repeat
  
  
–結果の一覧表示
  
choose from list bList with prompt “検索結果:”
  
end tell

–与えられたmailboxのフルパスをテキストで返す
on getFullPath(mBox)
  tell application “Mail”
    set fullPath to name of mBox
    
    
repeat
      set mBox to getParent(mBox) of me
      
      
try
        set aClass to class of mBox
      on error
        exit repeat
      end try
      
      
if aClass is not equal to mailbox then
        exit repeat
      end if
      
      
set aName to name of mBox
      
set fullPath to aName & “/” & fullPath
    end repeat
  end tell
  
  
return fullPath
end getFullPath

–親フォルダを取得
on getParent(aMailBox)
  tell application “Mail”
    tell aMailBox
      try
        set a to properties
        
set b to container of a
      on error
        return {“”, “”}
      end try
    end tell
  end tell
  
return b
end getParent

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/08/14 Twitter.app投稿テスト1

Twitterの公式Mac OS X用クライアント「Twitter.app」(Mac AppStoreから無料ダウンロード)を使ってTwitterに投稿を行うAppleScriptです。

Twitter.app自体はAppleScriptに対応しているものの、投稿のための命令が存在していなかったので(ASに解放されている機能はタイムラインなどの情報取得)、GUI Scripting経由で投稿を行わせてみました。

Twitter.appにユーザーアカウントとパスワードが設定してあって、ネットワーク接続が行えていることが実行の前提条件です。また、GUI Scriptingがオンになっている必要があります。

スクリプト名:Twitter.app投稿テスト1

postTweet(“テスト投稿 from AppleScript+Twitter.app”) of me

–Twitter.appでTwitter投稿を行う(GUI Scripting経由)
–GUI Scriptingがオンになっていることが実行条件
on postTweet(aMessage)
  activate application “Twitter” –activateを行わないとうまく動かない….
  
tell application “System Events”
    tell process “Twitter”
      
      
–File > New Tweet …
      
click menu item 1 of menu 1 of menu bar item 3 of menu bar 1
      
      
tell text area 1 of window 1
        set value to aMessage
      end tell
      
      
–Tweet button
      
click button 2 of window 1
      
    end tell
  end tell
end postTweet

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/08/13 OS X 10.7, Lionで複数GUIログインしてユーザー間アプリ制御

OS X 10.7, Lionからはネットワーク経由での複数GUIログインができるようになりました。つまり、1台のLionのMacに対して、LAN経由でWindowsとかiPadなどの上で動くVNCソフトウェアから同時にログインしてMac上のGUIアプリを使えるようになっています。

lion_share.jpg

GUIログインしたユーザーアカウントで、それぞれアプリケーションを起動して、1台のMacの中で複数のユーザー環境間でAppleEventを投げ合ってアプリケーションをコントロールすることができるようになりました。

じゃあ、現在のユーザーアカウントから、「画面共有」アプリをコントロールして、複数GUIログインを実行できないのか? 複数GUIログインを行うプロセスそのものもAppleScriptで自動化してしまえば、実行するのに時間がかかって一度に1つしか起動できないアプリケーション(InDesignとか、Illustratorとか、SAPとか、、)を1台のマシン上で同時に複数起動。1つのAppleScriptから複数のアプリケーションを同時にコントロールするようなことができるんじゃないか? と、考えていろいろ試してきました。

結論からいえば、「すべての手順を自動化できてはいない。けれど、複数のGUIログインしたユーザー環境の間でアプリケーション制御はできている」というのが現状です。

share_local.jpg

まず、普通に「画面共有」アプリにlocalhost接続を命令すると「自分の画面は共有できません」といってハネられます。しかし、ポート番号をつけてやるとその制限を無視されるようになります。

AppleScriptエディタ上で「open location “vnc:localhost:5901″」などと書いて実行すると、現在使用中のマシンに対して「画面共有」アプリ経由で多重ログインが行えます。

# ポート番号は5900〜6000の間で使用可能(vnc用にリザーブされているポート番号)
# 「vnc://localhost」ではなく「vnc:localhost」でした(転記ミス)

vn1.jpg

ユーザー認証が通ると、画面を切り替えるか、仮想画面を使用するか聞いてくる。仮想画面を選択すると……

vn2.jpg

現在のMacの画面内に、同じMacに別アカウントからアクセスするウィンドウがオープンする。

ここまできちんと機能が実装されていながら、「画面共有」でlocalhost(自分)を指定するとハネられる仕様になっているのは、動作に一部問題があるから? 別アカウントのユーザー環境を操作しようとすると、レインボーカーソルが回りっぱなしになってリモート側の操作が行えなくなることが多々ありました(電源ボタンを押してダイアログを出すと解除された)。

ユーザー名「user1」、パスワード「user1」というアカウント(同一マシン上)に対してリモート制御。すでに起動してあるiCalに対してコントロール。これを行う前に、かならず「システム環境設定」の「共有」で「リモートアップルイベント」をオンにしておく必要がある。

別ユーザー上のiCalに「hiyoko」という新しいカレンダーを作成してみたところ。

sh10.jpg

別ユーザーのiCal上に「hiyoko」カレンダーが作成された。LANごしにコントロールしているわけではないので、スピードも速い。

sh1.jpg

sh2.jpg

「画面共有」アプリのウィンドウを縮小したところ。別アカウントのウィンドウが左側に小さく表示されている。

sh3.jpg

「画面共有」アプリのウィンドウはクローズしてしまっても、ログイン状態は維持される。ためしにクローズしてみても……

sh4.jpg

別ユーザーアカウント「user1」「user2」はログイン状態が維持されていることが分かります。

1台のMac上で複数ユーザーアカウントのログインを行い、それぞれの環境でGUIアプリケーションを起動してアプリケーション制御を行う……といっても、扱いはLANごしに別マシン上の別アカウント上のアプリケーションをコントロールしているのと同じです。

そのため、リモートのAppleEventを受け付けるアプリケーションでないと直接は制御できない。最近は、セキュリティ維持のためにリモート制御を受け付けないアプリケーションが増えた。

しかし、制御先のユーザー環境にAppleScriptで作ったアプレットを起動しておいて、そのアプレット経由でコントロールするようにすれば、リモート制御非対応のアプリもコントロールできます。

こんな感じで、あり余るCPUパワーを生かすようなシステムを作りやすくなってきた……のかもしれません。

2011/08/11 リスト同士の引き算、足し算

リスト同士の引き算、足し算を行うAppleScriptです。

リストの要素数が同じことが前提条件です。

要素数が多いリストの演算だとOSAXを併用することも考えたいところですが、それほど多くないものであれば、けっこうこんな簡単なルーチンを用意して処理させても大丈夫です。

スクリプト名:リスト同士の引き算、足し算
set aList to {15.07, 9.275, 18.63, 19.45}
set bList to {15.381, 11.6, 18.601, 15.7}

set aRes to listSubstraction(aList, bList) of me
–> {-0.311, -2.325, 0.029, 3.75}

–リスト同士の引き算
on listSubstraction(aList, bList)
  –アイテム数をくらべて、合っていなければエラー
  
set aLen to length of aList
  
set bLen to length of bList
  
if aLen is not equal to bLen then return {}
  
  
set aResList to {}
  
repeat with i from 1 to aLen
    set itemA to contents of item i of aList
    
set itemB to contents of item i of bList
    
    
set tmpR to itemA - itemB
    
set the end of aResList to tmpR
  end repeat
  
  
return aResList
  
end listSubstraction

–リスト同士の足し算
on listAddition(aList, bList)
  –アイテム数をくらべて、合っていなければエラー
  
set aLen to length of aList
  
set bLen to length of bList
  
if aLen is not equal to bLen then return {}
  
  
set aResList to {}
  
repeat with i from 1 to aLen
    set itemA to contents of item i of aList
    
set itemB to contents of item i of bList
    
    
set tmpR to itemA + itemB
    
set the end of aResList to tmpR
  end repeat
  
  
return aResList
  
end listAddition

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/08/11 OS X 10.7, LionのRelease Noteに書かれていない変更点

OS X 10.7, Lionのチェック用にMacBook Air 11インチを持ち歩いていろいろ調べていますが…………今日、いろいろリリースノートに書かれていない重大な変更点に気づいてしまいました。

・URL Access ScriptingとKeychain Scriptingが廃止に

たしかに、URL Access Scriptingの出来はあまり褒められたものではなく(ダウンロードするファイルのファイル名の長さに32文字の制限があるとか)、どうせshell commandの「curl」の方がUser Agentの詐称もできるし、並列ダウンロードもできるしで、たまーにしか使っていませんでした。

Keychain Scriptingは……代わりがないと困ると思うのですが……調べてみると、「security」コマンド(/usr/bin/security)があるようで。これまたshell command経由で呼び出すライブラリでも作っておけばよいでしょうか。Keychainにアクセスするような機会はそれほど多くないので、代替手段があるのならダメージはあまりない、といったところ。

ただ……そんな重要な変更点は、リリースノートに明記してほしいのですが>Apple

「なんちゃらScripting」とか「なんたらevents」というのは、Classic Mac OS(Mac OS 8のころ?)の時代に追加されたもので、シェルコマンドが充実したMac OS X環境においては……シェルコマンドのラッパーとしてしか期待していなかったので、command shell経由で呼び出す先があるのであれば、それはそれでいいのかな、などと思うところ。

状況と推測を表にまとめてみました(↓)。

scripting.jpg

基本的に、shell commandのラッパーはshellをそのまま使え、というのがAppleの方針だと仮定すると、sipsコマンドのラッパーであり、かつsipsよりも低い機能しか提供できていないImage Eventsが廃止の最右翼のような気もしますが……画像処理は目玉機能にしている感じなので、あえて搭載を続ける可能性も。

となれば……Database Eventsあたりが……普通のリストやレコードに対して、フィルタ参照を行えるようにすれば、その方が有用性が高いと思います。AppleScriptのフィルタ参照機能で絞り込みを行う専用のDatabase Eventsといっても……正直なところ、それほど利用する機会はありません。

2011/08/08 数字の文字だけが入っているかどうかをテストする

与えられたデータに数字の文字だけが入っているかどうかをテストするAppleScriptです。

タブ区切りテキストのファイルをparseして、リストに変換する際、数字だけ入っているフィールドに関しては数値に変換する、という処理を行う際に作成したものです。

スクリプト名:数字の文字だけが入っているかどうかをテストする
set aList to {"2ひよこ2", "2.0", "-3"}
set newList to {}

repeat with i in aList
  
  
set aRes to detectContainsOnlyNumChar(i) of me
  
if aRes = true then
    set the end of newList to i as number
  else
    set the end of newList to (contents of i)
  end if
end repeat

newList
–> {"2ひよこ2", 2, -3}

–数字の文字だけが入っているかどうかをテストする
–数字の文字のみから構成されていたらtrue、1文字でもそれ以外のものが入っていたらfalse
on detectContainsOnlyNumChar(testText)
  –ANK文字列(大文字小文字は問わない)
  
set ankChar to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "-"}
  
  
set _testChar to testText as Unicode text
  
  
ignoring case
    repeat with i in _testChar
      set j to contents of i
      
if j is not in ankChar then
        return false
      end if
    end repeat
  end ignoring
  
  
return true
end detectContainsOnlyNumChar

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2011/08/07 Photoshop CS3でファイルパスがらみのバグが……

InDesign CS3で書き出したEPSをPhotoshop CS3でオープンして、InDesign CS3上で取得しておいた座標値をもとにPhotoshop CS3上でEPSの該当部分を切り抜いてJPEGで保存……という、他愛もないAppleScriptをMac OS X 10.4上で作っては大量の画像切り抜きを自動で行わせていたのですが……

なんと、Photoshop CS3をAppleScriptから操作したときに「一定階層以上のフォルダより下にあるファイルをオープンできない」というバグに遭遇しました。フルパスの長さが一定の文字数を超えると、

 error ”Adobe Photoshop CS3 でエラーが起きました:ファイル 不特定のオブジェクト が見つかりませんでした。” number -43

というエラーメッセージが表示されるばかり。原因がまったく分かりませんでした。

解決にまる1日以上かかってしまいましたが…………「1階層上のフォルダを経由してEPSをオープンさせると問題ない」ことに気づき、深く脱力。

 「もしかしたら、ほかにもあるかもしれない?」

Twitter上に情報を流してみたら、反応がありました。さらにすごいバグを教えていただけました……

「フルパス中に全角の数字あるいは全角のアルファベットが入っているとPhotoshopからファイルのオープンができない」

…………アドビのバグには慣れっこになっていたつもりでしたが、まだまだ甘かったようです。せめて、最新のPhotoshop CS5.5(使ったことがない)上とか、Adobe Photoshop Elements 9 Editor(使ったことがない)上では再現しないことを祈るばかりです。

2011/08/07 AppleScript中にファイルパスを記述する

いろいろ質問が寄せられていたり、US Appleのメーリングリストでも投稿数を統計的に分析(数えるだけ)すると明らかに傾向が出ています。AppleScriptに関する初心者の質問のほとんどがファイルパスに関するものです。

dd5.jpg

AppleScript上でのパスの書き方、扱い方については以下の記事を参照してください。ここでは、楽に記述する方法(作業方法)について述べます。

■AppleScriptで扱う「パス」について(1)
■AppleScriptで扱う「パス」について(2)
■AppleScriptで扱う「パス」について(3)
■AppleScriptで扱う「パス」について(4)

とくに、Mac OS Xでは(10.2あたりから?)ローカライズド・ファイルシステムが採用されているので、Finderで見ているとおり「デスクトップ」などとフォルダ名を書きたくなるところですが、実際の名前とは異なる(本当のフォルダ名は英語で「Desktop」)ためAppleScriptにエラーを返されたりと、使いやすさの実現のために実装されている機能のかずかずが仇になることもあるようです。

そこで、AppleScriptのプログラム中にファイルパスを記述する簡単な方法をご紹介しましょう。たいして難しくもなければ、悩ましいこともありません。単なる「作業」でしかありません。

やりかたは、大きくわけて2つ(私は(2)しか使いませんが、、、)。

(1)ドラッグ&ドロップ コース

Finder上でパスを調べたいファイルを選択して、AppleScriptエディタの記述エリアにそのままドラッグ&ドロップします。

dd1.jpg

すると、POSIX pathがAppleScriptエディタに入るので……

dd2.jpg

これをそのまま使用してみましょう。

スクリプト名:パスの書き方1
set a to “/Users/maro/Desktop/ScriptingBridgeFramework.pdf”
set aPath to (a as POSIX file)

–> file “Cherry:Users:maro:Desktop:ScriptingBridgeFramework.pdf”

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

こんな感じでパスを取得できます。fileのままだと受け付けないアプリケーションも多いので、「as alias」でalias(ファイル参照)にcastしてから使うとなおよいでしょう。

スクリプト名:パスの書き方1
set a to “/Users/maro/Desktop/ScriptingBridgeFramework.pdf”
set aPath to (a as POSIX file) as alias

–> alias “Cherry:Users:maro:Desktop:ScriptingBridgeFramework.pdf”

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

(2)コピー&ペースト コース

なにげにAppleScriptエディタ上に1行だけ書いて実行。ファイルのパスを求めたいときには「choose file」を、フォルダのパスを求めたい場合には「choose folder」とだけ書いて実行すればOKです。

dd3.jpg

実行してファイルを選ぶと「結果」欄にファイルのパスが表示されるので、

dd4.jpg

おもむろにパスの前後についているダブルクォートで囲まれている部分まで選択して、コピー。

別のScript(パスを書いておきたいScript)にペーストしてそのまま使用。

まあこんなもんでしょう。ただし、as aliasでファイル参照情報に変換する際に、参照先のファイルが存在しない(消したとか、名前を変更したとか)場合にはエラーになるので、try〜end tryのエラートラップを仕掛けて実行するのが大人な書き方です。

もっと「大人な」書き方をすれば、「path to」でホームディレクトリの位置を求めて、そこから相対的にどの位置にあるかといった情報を計算(文字列としてただつなぐだけ)して求めることになりますが、単に自分だけが使う「使い捨てScript」でそれほど気を使う必要もない、ということであれば……このような記述でも手っ取り早くてよいのではないでしょうか。

別のユーザー環境では動かないこと必至(HDD名称やユーザー名が違う)なので、こういうラフな書き方をしたAppleScriptは他人に配布しないように注意してください。あるいは、property文でプログラムの冒頭にまとめて書いて定義しておいて、他人の環境ではそこを直すようにコメントしておくとか。

スクリプト名:パスの書き方2
set b to “Cherry:Users:maro:Desktop:ScriptingBridgeFramework.pdf”
set bPath to b as alias

–> alias “Cherry:Users:maro:Desktop:ScriptingBridgeFramework.pdf”

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に