Archive for the 'フィルタ参照(filter reference)' Category

2017/09/30 VPN接続、接続解除

システム環境設定の「ネットワーク」で定義しておいたVPN接続サービスに対して接続、接続解除を行うAppleScriptです。

vpn1.png

VPN接続サービスの定義自体はAppleScriptから新規定義することはできません(無理やり、GUI Scripting経由でやればできなくはありませんが)。システム環境設定の「ネットワーク」であらかじめ作成しておくことになります。

VPN接続についてはメニューバー上に接続メニューを出しておけますが、メニューから操作するのとほぼ同様の機能を実行します。

vpn2.png

とりあえず、各ネットワークサービスがどのような設定値を持っているのか、持っていないのかを調べておきます。

vpn0.png

スクリプトエディタ上でログ表示状態にしておいて、スクリプトエディタ上でAppleScriptを実行すれば、スクリプトエディタ上に実行ログが表示されます。このあたり、別のプログラム(Folder Actionなど)で実行中のログまでは表示されません。

AppleScript名:Network Interfaceの調査
– Created 2017-09-30 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4860

tell application “System Events”
  tell current location of network preferences
    set sList to every service
    
repeat with i in sList
      set aP to properties of i
      
set aInt to interface of aP
      
try
        set bP to speed of aInt
        
log bP
      end try
    end repeat
  end tell
end tell

★Click Here to Open This Script 

ここで、各ネットワークサービスの種別が番号で取得できて、VPNを定義しておいたネットワークサービスが13番を返してきたので、種別が13のものをVPNと見立てて、接続および接続解除を行います。

存在しているVPNサービスを調査して、該当するものを取得する、というのは実にAppleScriptらしい処理です。ここで、単にサービス名称を文字列で指定してconnect/disconnectするというScriptだと、まったくAppleScriptのメリットを活かせないうえに、環境が変わると当然VPN名称も変わってくるので、実行できないことになってしまいます。

VPN接続自体をScriptから制御することに意味があるかどうかという話はありますが、未接続かどうかをチェックし、接続していなかったら再接続を行い、VPN接続先のMacのドライブをマウントしたり、vncによる画面共有を開始したり、リモート先で起動しているAppleScriptアプレットにコマンドを送ったりという運用ができることになります。作って実際に動かしてみたら、「十分に意義はある」と感じられました。

AppleScript名:VPN経由でネットワーク接続
– Created 2017-09-30 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4860

tell application “System Events”
  tell current location of network preferences
    set sList to every service whose kind is 13 –種別がVPNのNetwork Service
    
if sList = {} then return false
    
    
if length of sList is not equal to 1 then
      –複数VPNが存在している場合にはユーザー選択
      
set ssRes to choose from list sList
      
if ssRes = false then return false
      
set sRes to first item of ssRes
    else
      set sRes to first item of sList
    end if
    
    
connect sRes
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:VPN経由のネットワーク接続を切断する
– Created 2017-09-30 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4860

tell application “System Events”
  tell current location of network preferences
    set sList to every service whose kind is 13 and active is true –種別がVPNのNetwork Serviceで状態がactive(接続中)のものを取得
    
if sList = {} then return false
    
    
if length of sList is not equal to 1 then
      –複数VPNが存在している場合にはユーザー選択
      
set ssRes to choose from list sList
      
if ssRes = false then return false
      
set sRes to first item of ssRes
    else
      set sRes to first item of sList
    end if
    
    
disconnect sRes
  end tell
end tell

★Click Here to Open This Script 

しかし、System EventsまわりはAppleScript用語辞書に書かれていても実際には値を返してこない項目(speedとか)のオンパレードなので、Appleにはちゃんと仕事をしてほしいところです。

2017/09/03 ソフトウェア的にアンマウントしたが物理的に接続解除していないDriveの名前を取得

Finder上でいったんマウントしたあと、アンマウントしてもMac本体に物理的に接続したままのドライブ名を取得するAppleScriptです。

  「こんなもの、誰が何のために使うんだろうか?」

と首をひねる内容ですが、自分のところにも以前に1回だけこのような質問が来たことがあります。このScriptのオリジナルはShane Stanleyによるものですが(魔改造してブラッシュアップしていますが)、どういうニーズから作ったのか本人に聞いてみたいものです。

Finder経由でドライブ情報を取得するかぎりではそういう状態にあるドライブの存在を知ることは不可能ですが、diskutil経由で取得できるというのは意外でした。そういう意味では純粋なAppleScriptで書くことも可能ですが、おそらくこの倍ぐらいの長さになるものと思われます。

一応、macOS 10.13 Beta上でテストしてありますが、RAM 4GBのMacBook Airでは何かFinderの動作が不安定でいろいろクラッシュしています。

HFSおよびAPFSのドライブには対応していますが、macOSでマウント可能な他のフォーマットのドライブすべて(HFS、HFS Plus、APFS、WebDAV、UDF、FAT、ExFAT、SMB/CIFS、AFP、NFS、FTP、Xsan、NTFS、CDDAFS、ISO9660)を試せているわけではないので、実用的なレベルに持っていくためにはそのあたりを攻める(実際にテストして拡充させる)必要があることでしょう。

AppleScript名:ソフトウェア的にアンマウントしたが物理的に接続解除していないDriveの名前を取得
– Created 2017-06-20 Shane Stanley
– Modified 2017-06-20 Takaaki Naganoya
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4802

property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableSet : a reference to current application’s NSMutableSet
property NSMutableArray : a reference to current application’s NSMutableArray

set dRes to retUnmountedAndUnremovedDriveNames() of me
–> {”Data”}

on retUnmountedAndUnremovedDriveNames()
  set theResult to do shell script “diskutil list -plist”
  
  
– make dictionary from property list
  
set theResult to NSString’s stringWithString:theResult
  
set pListDict to theResult’s propertyList()
  
  
– extract relevant info
  
set disksAndParitions to pListDict’s objectForKey:“AllDisksAndPartitions”
  
  
set partitionsArray to NSMutableArray’s array() – to store values
  
repeat with anEntry in disksAndParitions
    set thePartitions to (anEntry’s objectForKey:“Partitions”)
    
if thePartitions = missing value then – no partitions means a volume
      (partitionsArray’s addObject:anEntry)
    else
      (partitionsArray’s addObjectsFromArray:thePartitions)
    end if
  end repeat
  
  
– filter by Content type
  
set thePred to NSPredicate’s predicateWithFormat:“Content == ’Apple_HFS’ OR Content == ’Apple_APFS’ “
  
set partitionsArray to partitionsArray’s filteredArrayUsingPredicate:thePred
  
  
– get names
  
set dList to (partitionsArray’s valueForKey:“VolumeName”) as list
  
  

  
set edList to retEjectableDriveNames() of me
  
set the end of edList to retBootDriveName() of me
  
set the end of edList to missing value –消し込みのために追加
  
  
set aSet to NSMutableSet’s setWithArray:dList
  
set bSet to NSMutableSet’s setWithArray:edList –Boot drive & local ejectable drives
  
  
aSet’s minusSet:bSet –補集合
  
set dRes to aSet’s allObjects() as list
  
  
return dRes
end retUnmountedAndUnremovedDriveNames

on retEjectableDriveNames()
  tell application “Finder”
    try
      set dList to name of every disk whose ejectable is true
    on error
      set dList to {}
    end try
  end tell
  
return dList
end retEjectableDriveNames

on retBootDriveName()
  tell application “Finder”
    return name of startup disk
  end tell
end retBootDriveName

★Click Here to Open This Script 

2017/04/21 GUI ScriptingによるTouchBarへのアクセス

Bill CheesemanがAppleScript Users MLに投稿したところによると、GUI Scriptingを用いてTouchBarへのアクセスに成功したとのこと。

TouchBar自体は価格が高く、本体価格が3万円近く上昇してしまうためMacBook Pro 15インチモデルにおいても近い将来TouchBarなしモデルの発売が検討されているなど、微妙な存在ではありますが・・・あると便利な気がします。

Bill Cheesemanによる本テストスクリプトは、ハードウェアとしてのTouchBarが存在する、あるいはソフトウェアベースのエミュレータ(TouchBar Serverなど)が存在する環境で実行すると、その情報を取得してくれます。

touchbar_resized.png

{minimum value:missing value, orientation:missing value, position:{80, 0}, class:pop up button, role description:”touchbarポップオーバー”, accessibility description:”文字ピッカー”, focused:false, title:”", size:{72, 30}, value:missing value, help:”特殊文字や絵文字を選択するときにタップします”, enabled:true, maximum value:missing value, role:”AXPopUpButton”, entire contents:{}, subrole:missing value, selected:missing value, name:missing value, description:”文字ピッカー”}

得られた情報を確認してみると、いろいろと興味深い情報が返ってきているようです。

touchbar2.png

TouchBarが存在しない環境で実行すると、

toucherror.png

のように、エラー情報を表示します。ダイアログ表示はテストScript側で行っているものであり、表示せずにスルーするのが実際の使い方になるはずです。

なお、本Scriptを実行する際にはシステム環境設定の「セキュリティとプライバシー」の「プライバシー」でアクセシビリティの項目に「スクリプトエディタ」を登録しておく必要があります(GUI Scriptingのいつものお約束)。

AppleScript名:TouchBarの検出デモ
– Touch Bar GUI Scripting Demo
– 1.0.0 Bill Cheeseman 2017-04-21

– Usage: Run this script in Script Editor or Script Debugger to see its Touch Bar information.

property touchBarRole : “AXFunctionRowTopLevelElement”

tell application “System Events”
  set frontApp to first application process whose frontmost is true
  
try
    set touchBar to first UI element of frontApp whose role is touchBarRole
  on error errMsg number errNum
    display alert “No Touch Bar support” message “This application or the Mac running it does not support the Touch Bar, or accessibility has not been allowed for script runner in the Security & Privacy pane of System Preferences.

& errMsg & space & errNum
    return
  end try
  
set touchBarItems to value of attribute “AXChildren” of touchBar
  
return properties of item 1 of touchBarItems
  
–> {minimum value:missing value, orientation:missing value, position:{80, 0}, class:pop up button, role description:”touchbarポップオーバー”, accessibility description:”文字ピッカー”, focused:false, title:”", size:{72, 30}, value:missing value, help:”特殊文字や絵文字を選択するときにタップします”, enabled:true, maximum value:missing value, role:”AXPopUpButton”, entire contents:{}, subrole:missing value, selected:missing value, name:missing value, description:”文字ピッカー”}
end tell

★Click Here to Open This Script 

2016/11/18 Calendarで指定カレンダー、指定日のイベントを削除する

カレンダー.app(Calendar.app)で、指定カレンダーに登録されている指定日のイベントを削除するAppleScriptです。

カレンダー.app(旧称iCal)相手のScriptingは、「やってできないこともないが、落とし穴があちこちに空いているので、GUIアプリケーション相手に命令を投げるよりも、フレームワーク経由で操作したほうがいい雰囲気が漂っている」ものです。macOS標準装備のApple純正アプリケーションの中でも、ダントツの出来の悪さが光ります。

カレンダー.appには罠や要注意点がゴロゴロしているので、出来の悪さを知っていないと悩むことになります。特定のOSバージョンに固有のバグが存在していたりもします。

仕様上の罠(1)カレンダーの特定

カレンダー.app上でイベントを特定する前に、どのカレンダーに登録されているイベントなのかを特定する必要があります。名称で指定することも可能ですが、「このMac内」(Local)と「iCloud」(クラウド上)など、複数の場所で同じ名前のカレンダーを作成できてしまうため、あらかじめカレンダーの「説明」欄に区別するためのテキストを入れておくなどして、任意のカレンダーを特定する必要があります。

仕様上の罠(2)イベントの特定

カレンダー上のイベントを特定するためには、開始日時と終了日時を指定して、その条件に合致するものだけを抽出(フィルタ参照)することになります。このフィルタ参照を書けるかどうかでイベントの特定が行えるかどうかが決まってきます。開始日時と終了日時という2つの条件をandで指定するフィルタ参照で、2つ目の条件記述に「of it」という書き方をしないと抽出ができませんでした。慣れが必要な部分なので、いきなりこれを書けと言われても困るところです。

さらに、イベントには開始〜終了日時が明確に記述されているものと、複数日にまたがるイベントや、毎週金曜日といった指定が行われているものもあります。こういうタイプのイベントが存在している場合には、別途対処する必要が出てきます(けっこうたいへん、というか無理。この手のプログラムを仕事で書く必要がある場合には仕様を決定する際に真っ先に「この手のイベントは例外とさせてほしい」と提案する箇所)。

仕様上の罠(3)イベントはリストで返ってくる

これはカレンダー.appにかぎったことではないのですが、フィルタ参照で抽出したオブジェクト(ここではイベント)は、1件かもしれないし、0件かもしれないし、100件かもしれません。結果はリストで返ってくるのでループで1件ずつ削除するか、あるいはフィルタ参照で抽出した結果に対して削除コマンドを実行することになります。

仕様上の罠(4)イベントを削除しても、すぐには画面に反映されない

AppleScriptからカレンダー.app上のイベントを削除しても、画面表示にすぐには反映されません。このため、いったんカレンダー.appを終了させて、ふたたび起動することで確認できました。reload calendar命令もあるので、実行すると最新の状態に更新してくれるのかと思ったものの、いったん終了しないと削除された状態を確認できませんでした。

これはキツい(^ー^;;;;

AppleScript名:Calendarで指定カレンダー、指定日のイベントを削除する
– Created 2016-11-18 by Takaaki Naganoya
– 2016 Piyomaru Software
–http://piyocast.com/as/archives/4321
use AppleScript version “2.4″
use scripting additions

set sDat to “2016/11/21 0:00:00″
set eDat to “2016/11/22 0:00:00″
set sDatO to (date sDat)
set eDatO to date eDat

tell application “Calendar”
  –対象となるカレンダーを特定
  
set cList to every calendar whose name is “ぴよまるソフトウェア” and description is equal to “iCloud”
  
if cList = {} then return
  
set theCalendar to first item of cList
  
  
–カレンダー内のイベントを特定
  
tell theCalendar
    set eList to every event whose start date sDatO and end date of it < eDatO
  end tell
  
  
–削除する(一括削除もできるが、デバッグのために1件ずつ削除)
  
repeat with i in eList
    set j to contents of i
    
delete j
  end repeat
  
  
quit
end tell

delay 1

tell application “Calendar”
  activate
end tell

★Click Here to Open This Script 

2016/10/17 リマインダーで指定のリスト内の終わっていない項目をテキストで返す

リマインダー(Reminders.app)の指定名称のリスト内で、まだ終わっていない(completedがfalseな)項目をテキストで返すAppleScriptです。

リマインダーでさまざまなToDo管理を行っており、その一環としてマンガの発売予定日をまとめたリストがあるのですが、

rem1_resized.png

項目を選んでコピーし、普通にテキストエディタ(ここではmiを利用)にペーストすると、

rem2_resized.png

のようになります。親切というか、出来過ぎというか、不必要な情報まで入ってくるので、もっとシンプルに名称だけ取得してくれたほうが使い勝手がいいと思います(個人の意見です)。

そこで、さくっとAppleScriptを組んで件名(name)をテキストとして連結して取得できるようにしてみました。

AppleScript名:リマインダーで指定のリスト内の終わっていない項目をテキストで返す
– Created 2015-10-14 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4278

tell application “Reminders”
  tell list “マンガの発売予定日”
    set rList to name of every reminder whose completed is false
    
–リマインド項目は以下のように返ってくる
    
–>{{due date:date “2016年10月19日水曜日 12:00:00″, modification date:date “2016年10月5日水曜日 11:08:23″, class:reminder, body:missing value, completed:false, id:”x-apple-reminder://05A1CBEE-0737-47A7-ABB9-9AF582A604AF”, creation date:date “2016年10月5日水曜日 11:05:36″, name:”87 Clockers 9巻 2016/10/19″, completion date:missing value, container:list id “FA4B1A00-E1AD-4E81-921F-FDCBC7191FA3″ of application “Reminders”, priority:0, remind me date:date “2016年10月19日水曜日 12:00:00″}…. }
  end tell
end tell

set aRes to retStrFromArrayWithDelimiter(rList, return) of me
–>
(*
“87 Clockers 9巻 2016/10/19
マリアージュ 神の雫最終章 5巻 2016/10/21
女騎士、経理になる。 3巻 2016/10/24
王様達のヴァイキング 11巻 2016/10/28
アオイホノオ 16巻 2016/11/12
ピアノのムシ 9巻 2016/11/16
機動戦士Zガンダム Define 12巻 2016/11/19
MIX 10巻 2016/12/8
大砲とスタンプ 6巻 2016/12/21
スティーブス 6巻 2017/1/13
甘城ブリリアントパーク 9巻 2017/2/15
宇宙兄弟 30巻 2017/2/24
ヒーローカンパニー 10巻 2017/6/6″
*)

–リストを指定デリミタをはさんでテキスト化
on retStrFromArrayWithDelimiter(aList, aDelim)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set aRes to anArray’s componentsJoinedByString:aDelim
  
return aRes as text
end retStrFromArrayWithDelimiter

★Click Here to Open This Script 

2016/10/05 SDカードを検出

マウント中のドライブのマウントポイント(例:/Volumes/ドライブ名)を指定すると、当該ドライブがSDカードかどうかを判定するAppleScriptです。

system_profilerコマンドを呼び出して詳細な情報を取得して判定するようにして、手元にある2枚のSDカードはこれで判定できています。MacBook Pro Retina 2012の内蔵SDカードリーダー、およびUSB接続のSDカードリーダーの両方で判定できることを確認しています。

system_profilerの実行結果は、

mountedsd1.jpg

MacBook Pro Retina内蔵SDカードリーダーに入れたSDXCカード「JVCCAM_SD」
-> {writable:”yes”, _name:”JVCCAM_SD”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, size_in_bytes:3.3179041792E+10, bsd_name:”disk3s1″, free_space_in_bytes:2.0819673088E+10, mount_point:”/Volumes/JVCCAM_SD”, physical_drive:{is_internal_disk:”yes”, device_name:”Built In SDXC Reader”, protocol:”Secure Digital”, media_name:”Apple SDXC Reader Media”, partition_map_type:”master_boot_record_partition_map_type”}}

USB接続SDカードリーダーに入れたSDカード「RICOHDCX」
–> {writable:”yes”, _name:”RICOHDCX”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, volume_uuid:”3BD2F468-5B42-327C-AB96-2812A02E7126″, size_in_bytes:7.939817472E+9, bsd_name:”disk4s1″, free_space_in_bytes:5.415960576E+9, mount_point:”/Volumes/RICOHDCX”, physical_drive:{is_internal_disk:”no”, device_name:”SD Transcend”, protocol:”USB”, media_name:”TS-RDF5 SD Transcend Media”, partition_map_type:”master_boot_record_partition_map_type”}}

mountedsd2.jpg

MacBook Pro Retina内蔵SDカードリーダーに入れたSDカード「RICOHDCX」
–> {writable:”yes”, _name:”RICOHDCX”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, volume_uuid:”3BD2F468-5B42-327C-AB96-2812A02E7126″, size_in_bytes:7.939817472E+9, bsd_name:”disk3s1″, free_space_in_bytes:5.415960576E+9, mount_point:”/Volumes/RICOHDCX”, physical_drive:{is_internal_disk:”yes”, device_name:”Built In SDXC Reader”, protocol:”Secure Digital”, media_name:”Apple SDXC Reader Media”, partition_map_type:”master_boot_record_partition_map_type”}}

USB接続SDカードリーダーに入れたSDXCカード「JVCCAM_SD」
–> {writable:”yes”, _name:”JVCCAM_SD”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, size_in_bytes:3.3179041792E+10, bsd_name:”disk4s1″, free_space_in_bytes:2.0819673088E+10, mount_point:”/Volumes/JVCCAM_SD”, physical_drive:{is_internal_disk:”no”, device_name:”SD Transcend”, protocol:”USB”, media_name:”TS-RDF5 SD Transcend Media”, partition_map_type:”master_boot_record_partition_map_type”}}

となっており、device_nameとmedia_nameを単語ごとにリスト化し、「SD」「SDHC」「SDXC」の単語が入っているかどうかを判定しています。

同一名称のSDカードが複数枚同時にマウントされた場合の挙動については検証していないため、そういうケースには対応しきれていないと思われます。ご注意ください。

AppleScript名:SDカードを検出
– Created 2016-10-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4251

tell application “Finder”
  set driveList to every disk whose format is (MSDOS format) and ejectable is true and startup is false
  
  repeat with i in driveList
    set myDisk to disk of (first item of i)
    
set myMountPoint to POSIX path of (myDisk as alias)
    
–> “/Volumes/JVCCAM_SD/”
    
–> “/Volumes/RICOHDCX/”
    
set sdRes to detectSDCard(myMountPoint) of me
    
–> true –SD Card, false –Not SD Card
  end repeat
end tell

on detectSDCard(myMountPoint as string)
  
  set resData to runCommandString(“system_profiler -xml SPStorageDataType”) of me
  
set aaDict to (readPlistFromStr(resData) of me) as list
  
set aDictList to (_items of first item of aaDict)
  
  repeat with i in aDictList
    set j to contents of i
    
    set aMountPoint to (mount_point of j) as string
    
–> “/Volumes/JVCCAM_SD”
    
–> “/Volumes/RICOHDCX”
    
    if aMountPoint is not equal to “/” then
      if ((aMountPoint & “/”) is equal to myMountPoint) then
        set aDevName to words of (device_name of physical_drive of j)
        
set aMediaName to words of (media_name of physical_drive of j)
        
        –SD/SDHC/SDXCのカード検出
        
set aDevF to (“SD” is in aDevName) or (“SDHC” is in aDevName) or (“SDXC” is in aDevName)
        
set aMediaF to (“SD” is in aMediaName) or (“SDHC” is in aMediaName) or (“SDXC” is in aMediaName)
        
        if (aDevF and aMediaF) then return true
      end if
    end if
  end repeat
  
  return false
end detectSDCard

–文字列で与えたシェルコマンドを実行する
on runCommandString(commandStr as string)
  set aPipe to current application’s NSPipe’s pipe()
  
set aTask to current application’s NSTask’s alloc()’s init()
  
aTask’s setLaunchPath:“/bin/sh”
  
aTask’s setArguments:{“-c”, current application’s NSString’s stringWithFormat_(“%@”, commandStr)}
  
aTask’s setStandardOutput:aPipe
  
set aFile to aPipe’s fileHandleForReading()
  
aTask’s |launch|()
  
return current application’s NSString’s alloc()’s initWithData:(aFile’s readDataToEndOfFile()) encoding:(current application’s NSUTF8StringEncoding)
end runCommandString

–stringのplistを読み込んでRecordに
on readPlistFromStr(theString)
  set aSource to current application’s NSString’s stringWithString:theString
  
set pListData to aSource’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aPlist to current application’s NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(current application’s NSPropertyListImmutable) |format|:(current application’s NSPropertyListFormat) errorDescription:(missing value)
  
return aPlist
end readPlistFromStr

★Click Here to Open This Script 

2016/10/04 SDカードと思われるドライブを抽出

Finderで、マウント中のドライブのうちSDカードと思われるものを抽出するAppleScriptです。

抽出条件は、

 ディスクフォーマット:MS-DOS
 イジェクト可能なドライブか?:はい
 起動可能なドライブか?:いいえ
 ローカルボリュームか?:はい

finder2.jpg

というだけで、それがSDカードかどうかという確実な判定は行なっていません。

「多分こういう条件を満たすドライブはSDカードなんだろー」

という方法によるものです(この条件だと、USBメモリーとかDOSフォーマットした外付けHDDも該当しますね)。Finder上ではSDカードのアイコンがついているので、OS的にはそれがSDカードであることは認識されています。

Finder上で抽出したドライブのpropertiesを取得すると、

{class:disk, name:”JVCCAM_SD”, index:5, displayed name:”JVCCAM_SD”, name extension:”", extension hidden:false, container:computer container of application “Finder”, disk:disk “JVCCAM_SD” of application “Finder”, position:{-1, -1}, desktop position:{-1, -1}, bounds:{-33, -33, 31, 31}, kind:”ボリューム”, label index:0, locked:false, description:missing value, comment:”", size:1.2359368704E+10, physical size:1.2359368704E+10, creation date:date “2014年2月14日金曜日 5:39:40″, modification date:date “2016年9月15日木曜日 12:26:26″, icon:missing value, URL:”file:///Volumes/JVCCAM_SD/”, owner:”maro”, group:”(不明)”, owner privileges:read write, group privileges:read write, everyones privileges:read write, container window:container window of disk “JVCCAM_SD” of application “Finder”, id:-105, capacity:3.3179041792E+10, free space:2.0819673088E+10, ejectable:true, startup:false, format:MSDOS format, journaling enabled:false, local volume:true, ignore privileges:true}

のように結果が帰ってきます。この中にはディスク種別に関する情報は含まれていないため、別途何かのOS内部のサービスを利用して判定できるのでしょう。

AppleScript名:SDカードと思われるドライブを抽出
– Created 2016-10-04 by Takaaki Naganoya
– 2016 Piyomaru Software
–http://piyocast.com/as/archives/4247
tell application “Finder”
  set aDrive to every disk whose format is (MSDOS format) and ejectable is true and startup is false and local volume is true
end tell
–> {disk “JVCCAM_SD” of application “Finder”}

★Click Here to Open This Script 

2016/06/06 Mail.appで複数のメッセージを移動させる

Apple純正のメーラーアプリケーション「Mail.app」にはmove命令があり、message(メール)を指定の(Mail.app上の)フォルダに移動させることが可能です(ファイルシステム上のフォルダではありません)。

1つのmessageを移動させる場合には問題ないのですが、複数のmessageを同時に移動させる場合にはエラーになります。

この挙動がおかしくなったと感じたのは、OS X 10.8だったと記憶しています。複数のmessageをリストに入れ、move命令で一気に移動させる処理でいきなりエラーが出るようになりました

複数のmessageを一度にmoveできると、アプリケーション側との通信頻度を下げられ、処理速度の面で大幅に有利になるため、対応してほしいところです。

Mail.appのAppleScript用語辞書にもmove命令の説明で、object(s) to move を指定できるという表記があり、

mailappdic.png

複数のメールを一度に移動できることを期待したいところです。

しかし、今日にいたってもリストに入れた複数のmessageは移動できず、にもかかわらずAppleScript用語辞書にはobject(s)と複数形で記載され続けるという状況が続いています。

そこで、自分が期待するところの「複数のmessage」と、Appleが定義するところの「複数のmessage」が実は異なるのではないか、と疑うようになりました。

 自分の定義:リスト型変数に入れた複数のmessage
 Appleの定義:上記以外の何か???

まず、現時点で動作する安全な表記を確認するとこのリスト(↓)のようになります。複数のmessageをリストに抽出し、ループで1つずつmoveします。

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに順次移動
tell application “Mail”
  tell inbox
    set mList to every message whose sender contains “notify@twitter.com”
  end tell
  
  repeat with i in mList
    set j to contents of i
    
ignoring application responses
      move j to mailbox “メルマガ/_個人的なメール/twitter”
    end ignoring
  end repeat
end tell

★Click Here to Open This Script 

ここで、フィルタ参照で取得したmessageのリストに対して直接move命令を実行するとエラーになります。

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに一括移動(動かないパターン)
tell application “Mail”
  set aList to (every message in inbox whose sender contains “notify@twitter.com”)
  
move aList to mailbox “メルマガ/_個人的なメール/twitter” –Error
end tell

★Click Here to Open This Script 

そこで、「リストに入れない状態で、しかも複数のmessageを与えてみたらどうなるだろう?」と考え、実際に書いて試してみました。

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに一括移動
tell application “Mail”
  move (every message in inbox whose sender contains “notify@twitter.com”) to mailbox “メルマガ/_個人的なメール/twitter”
end tell

★Click Here to Open This Script 

あれ? 一括移動できた(^ー^;;; しかし、リストに入れたりソートしたりユニーク化したものを渡せないと、ちょっと使い勝手がよくありません。

しばし悩んだものの、冗談半分で、

AppleScript名:受信フォルダ内の特定のメールアドレスから来たメールを別のフォルダに一括移動(動かないパターン) の解決
tell application “Mail”
  set aList to a reference to (every message in inbox whose sender contains “notify@twitter.com”)
  
move aList to mailbox “メルマガ/_個人的なメール/twitter” –OK
end tell

★Click Here to Open This Script 

と、a reference toで参照したものをリストに入れてみたところ、希望の動作を行いました(!!!!!)。

ただ、このリストを加工したりできないので、問題はあまり解決していないような、、、、

2016/05/17 Duet DisplayのMac側プロセスの死活判定を定期的に実行

iOS用のDuet Displayは、iOSデバイスをUSB経由でMac/Windows機とつなぎ、iOSデバイスを外部ディスプレイ化するアプリです。

AppStoreで1,900円(記事作成時現在の価格)でiOSアプリを購入+インストール、Mac側にはDuet Displayのホームページから無料ダウンロード可能なツールをインストールします。

このDuet Displayを使って、ちょっと使い道のなかったiPadなどをMacのサブディスプレイにすることができます。以前は、Air Displayを使って無線LAN経由でiPadを外部ディスプレイ化してもみましたが、パフォーマンスがいまいちなうえにソフトウェアの安定性が残念なレベルだったので使用をやめてしまいました。

img_3577_resized.png

Duet Displayはさすがにゲームを快適に行えるほどではありませんが、ちょっとした用途であれば違和感なく使えるパフォーマンス。

3・4日ほどは便利に使っていたものの、途中でMac側のduetプロセスがコケたりして、急にディスプレイ表示が消える現象に直面。サブディスプレイとして使っている場合には冗談ぐらいで済みますが、メインディスプレイとして使っていた場合には悲惨です(メインディスプレイとして使ってほしくない雰囲気)。

いろいろ実験(長期間起動させっぱなしとか、スリープからの回復を連続して行うとか)を行ったところ、duetプロセスが落ちることは避けられないという結論に至りました(落ちるものは落ちる)。

そこで、Mac側のduetプロセスの死活判定を定期的に行い、死んでいた場合には起動し直すAppleScriptアプレットを作って運用してみました。このプロセス死活判定は、以前に作成したものを使いまわしたため、プログラムはほぼ2〜3行追加しただけです。

このような仕組みを用意したおかげで、iPadをMac miniのメインディスプレイに設定して、duetプロセスがコケて表示が消えても自動で表示が回復するようになりました。idleハンドラ内のタイマー割り込み時間(秒)を現在は10秒にしていますが、もうちょっと短い間隔でチェックしてもよいと思います。

本AppleScriptはスクリプトエディタで「アプリケーション」として保存し、オプションで「ハンドラの実行後に終了しない」を指定することで立ち上げっぱなしの運用が可能です。また、Dock上から「ログイン時に開く」にチェックすることで、電源オン時→ログイン時に自動起動されるため、おすすめです。

AppleScript名:restartDuet
on run
  idle
end run

on idle
  set aRes to detectAppAliveByID(“com.kairos.duet”) of me
  
if aRes is not equal to true then
    tell application id “com.kairos.duet” to launch
  end if
  
return 10
end idle

–指定プロセスの死活判定(Bundle IDで判定)
on detectAppAliveByID(aProcBundleID)
  set aProcBundleID to aProcBundleID as string
  
  
–Phase 1 psコマンド経由で状態を確認してみる
  
set aRes to false
  
try
    tell application “System Events”
      set aList to bundle identifier of every process
      
if aProcBundleID is in aList then
        
        
set tmpList to name of every process whose bundle identifier = aProcBundleID
        
set aProcName to contents of first item of tmpList
        
        
set aRes to true
        
tell process aProcName
          set processID to unix id –プロセスIDを取得
        end tell
        
        
set processID to processID as string
        
        
–指定プロセスがゾンビプロセス化しているかどうかを判定
        
set procState to (words of (do shell script “/bin/ps “ & processID & ” | cut -d ’ ’ -f 6″)) as string
        
        
if procState contains “Z” then
          –ゾンビプロセスを殺す
          
do shell script “/bin/kill -9 “ & processID
          
return “killed”
        end if
      else
        –指定したBundle IDのプロセスが存在しない場合falseでリターン
        
return false
      end if
    end tell
  on error
    –異常発生時にはさっさとリターン
    
return false
  end try
  
  
–Phase 2 指定アプリに直接コンタクトして反応を見る
  
try
    with timeout of 3 seconds
      tell application id aProcBundleID
        set aTmpvar to name –これができない(Scriptableな)アプリはほとんどないだろう(多分)
      end tell
    end timeout
  on error
    –名称を取得できなかった場合にはコケていると見なしてプロセスを殺す
    
do shell script “/bin/kill -9 “ & processID
    
return “killed”
  end try
  
  
return aRes
end detectAppAliveByID

★Click Here to Open This Script 

2015/07/29 指定名称のアプリをフルスクリーン表示

指定名称のアプリケーションをフルスクリーン表示するAppleScriptです。フルスクリーン表示させるアプリケーションはあらかじめ起動しておく必要があります。

また、GUI Scriptingを用いているため、実行前にあらかじめ「システム環境設定」>「セキュリティとプライバシー」>「アクセシビリティ」で本Scriptを実行するアプリケーション(おそらく、スクリプトエディタ)にコンピュータの(GUI側からの)制御を許可するよう設定しておく必要があります。

この手のScriptはよく見かけるのですが、自分の環境でしか動かないものを公開している例が多く、少し汎用性と柔軟性をもたせてみました。ただし、フルスクリーン表示の解除はサポートしていません(終了させればよいのでは? ^ー^;)。

フルスクリーン表示はOS X 10.7で搭載されましたが、これをAppleScriptからコントロールするまっとうな手段は用意されてきませんでした。次善の策として(やりたくないけど、仕方なく)GUI Scriptingでコントロールすることになります。

アプローチ方法は主に3つ。

(1)メニューを操作してフルスクリーン化
(2)キーボードショートカットを実行してフルスクリーン化

本Scriptでは、

(3)ウィンドウのフルスクリーン/ズームボタンをクリック

を採用しています。

本Scriptで柔軟性を持たせたのは3点。

(1)アプリケーション名称に、本当の名前(name)でもローカライズされた名称(displayed name)でもどちらでも許容する

(2)フルスクリーン表示がサポートされていないアプリケーションでは、ウィンドウの最大化を行う

(3)メニューの表示項目名などを使っていないため、使用/実行言語環境に左右されない

といったところです。

buttons.png

本来は、アプリケーションのプロパティなり、

–> {frontmost:false, class:application, name:”Safari”, version:”9.0″, full screen:true}

ウィンドウのプロパティにフルスクリーン表示状態を示すものが存在して、

–> {document:document “AS Hole(AppleScriptの穴) By Piyomaru Software”, closeable:true, zoomed:true, class:window, index:1, visible:true, name:”AS Hole(AppleScriptの穴) By Piyomaru Software”, modal:false, miniaturizable:true, titled:true, id:88, miniaturized:false, floating:false, resizable:true, bounds:{26, 23, 901, 1195}, current tab:tab 1 of window id 88, zoomable:true, full screen:true}

その操作によりフルスクリーン/ズーム表示を変更できることが望ましいところです。ただし、最近のUS AppleのAppleScriptエンジニアリングチームは、ASOCでCocoaの機能を使ってScripter側に勝手にやらせようということが多く、Finderの「タグ」にしてもフルスクリーン表示にしても、OS側の新機能にキャッチアップしていない部分が多くて困ります。

printコマンドで出力プリンタ名称を指定できても、肝心のプリンタ名称を取得する手軽でまっとうな方法が標準命令に存在していない点などは、どうかしています。結局、Apple側の対応を待たずにいろいろScripter側でやっているわけで…

AppleScript名:指定名称のアプリをフルスクリーン表示
– Created 2015-07-29 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions

set a to “Safari”
set aRes to dispAppWithFullScreen(a) of me

–指定名称のアプリケーション(起動中のもの)をフルスクリーン表示
on dispAppWithFullScreen(applicationName)
  
  
set appName to returnExactNameOfAnApp(applicationName)
  
if appName = false then return false –指定のアプリがなかった、あるいはアプリケーション名を間違って指定した
  
  
set pID to id of application appName
  
  
tell application appName
    reopen
    
activate
  end tell
  
  
tell application “System Events”
    tell (process 1 whose bundle identifier is pID)
      set b1List to every button of window 1 whose subrole is “AXFullScreenButton” –Full Screen
      
set b2List to every button of window 1 whose subrole is “AXZoomButton” –Zoom
      
      
if b1List = {} then
        set targB to first item of b2List –フルスクリーン表示機能を持たないアプリの場合
      else
        set targB to first item of b1List –フルスクリーン表示機能を持つアプリの場合
      end if
      
      
click targB
    end tell
  end tell
  
  
return true
  
end dispAppWithFullScreen

–Displayed Nameでアプリケーション名が与えられた場合に、正しい名称を返す
on returnExactNameOfAnApp(aName)
  tell application “System Events”
    set ap1List to every process whose name is equal to aName
    
if ap1List = {} then
      set ap1List to every process whose displayed name is equal to aName
      
if ap1List = {} then return false
    end if
    
set anApp to contents of first item of ap1List
    
return name of anApp
  end tell
end returnExactNameOfAnApp

★Click Here to Open This Script 

2015/07/13 ASOCでDict書き込み_3(Bridge Plus)

完全に趣味の内容のAppleScriptを、Shane Stanleyがアップデートして送ってくれました(汗)。趣味のScriptというのは、完全に作り捨てに近いというか、あまり後先考えずに作ってしまうことが多いですが・・・

書き換えポイントは、

 (1)ASObjcExtras.frameworkではなくBridgePlusライブラリを使用
 (2)NSFileManagerでディレクトリを作成
 (3)パスの組み立て時に「URLByAppendingPathComponent:」を使うことにより、フォルダを示すパスの末尾に「:」がついていなかったとか、「::」といった誤った表記を行ってしまった場合に備えている

とのこと。do shell scriptなんてダサいぜ、的なShaneのご意見もありましたが、使って簡潔になる箇所(dateコマンドとか)は使ってもいいんじゃないかと。いえ、意見はとってもわかるんですけど、「使えるものはなんでも使う派」なんで(^ー^;;

OS X 10.11ではaliasやfileとNSURLが正確にBridgeされるようになるので、パスの取り扱いについては機能向上(簡潔な記述)が期待ができそうです。”NSURL”を書くと予約語とぶつかる、とかいうダサダサな仕様が直ることは10.11に期待したいです。

しかし、英語圏のShaneから正確な日本語データの入ったプログラムが送られてくると(リスト内のリンクをクリックすればScript Editorに転送されるのはわかっているとはいえ)、かなりビビります(^ー^;;

AppleScript名:ASOCでDict書き込み_3(Bridge Plus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus” – instead of ASOBjCExtras framework

load framework – BridgePlus command to load

set a1List to {“msName”, “sortieTimes”}
set b1List to {{“近 装甲強化型ジム 獲得済 COST: 200″, 66}, {“遠 ジム・キャノン 獲得済 COST: 160″, 43}, {“近 ザクII(F2) 獲得済 COST: 160″, 42}, {“近 ジム・コマンド 獲得済 COST: 200″, 32}, {“近 ジム(WD隊) 獲得済 COST: 160″, 28}, {“近 陸戦型ガンダム 獲得済 COST: 220″, 24}, {“近 ジム改 獲得済 COST: 240″, 22}, {“遠 ガンタンク 獲得済 COST: 200″, 22}, {“格 ジム(指揮官機) 獲得済 COST: 160″, 20}, {“近 ジム 獲得済 COST: 120″, 19}, {“遠 量産型ガンタンク 獲得済 COST: 160″, 14}, {“格 陸戦型ジム 獲得済 COST: 120″, 12}, {“格 ガンダム 獲得済 COST: 280″, 11}, {“近 ジム・トレーナー 獲得済 COST: 120″, 9}, {“射 ジム・スナイパーII(WD隊) 獲得済 COST: 220″, 9}, {“射 陸戦型ガンダム(ジム頭) 獲得済 COST: 200″, 7}, {“格 ガンダムEz8 獲得済 COST: 240″, 6}, {“近 ジム・寒冷地仕様 獲得済 COST: 200″, 6}, {“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, 6}, {“格 ジム・ストライカー 獲得済 COST: 180″, 4}, {“格 ガンキャノン重装型 獲得済 COST: 160″, 3}, {“近 アクア・ジム 獲得済 COST: 160″, 2}, {“射 ガンキャノン 獲得済 COST: 200″, 2}, {“近 ジム・コマンドライトアーマー 獲得済 COST: 160″, 1}, {“格 ボールK型 獲得済 COST: 120″, 0}, {“格 B.D.2号機 獲得済 COST: 260″, 0}, {“格 プロトタイプガンダム 獲得済 COST: 280″, 0}, {“近 パワード・ジム 獲得済 COST: 240″, 0}, {“射 デザート・ジム 獲得済 COST: 160″, 0}, {“遠 量産型ガンキャノン 獲得済 COST: 200″, 0}}

– BridgePlus uses SMSForder instead of SMSFord in ASOBjCExtras, but method is the same
set aArray to current application’s SMSForder’s subarraysIn:b1List asDictionariesUsingLabels:a1List |error|:(missing value)

set cRec to {msList:aArray, sortieDate:date string of (current date)}

set aName to “efsf.plist”
saveRecordToFolAsPlist(cRec, “戦場の絆”, aName) of me

on saveRecordToFolAsPlist(theRecord, folName, aName)
  
  
set myAppSupDir to POSIX path of (path to application support from user domain)
  
set folderURL to (current application’s class “NSURL”’s fileURLWithPath:myAppSupDir)’s URLByAppendingPathComponent:folName
  
  
–do shell script(mkdir -p)のかわりに、指定ディレクトリまで作成
  
current application’s NSFileManager’s defaultManager()’s createDirectoryAtURL:folderURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
  
  
set theDict to current application’s NSDictionary’s dictionaryWithDictionary:theRecord
  
set aRes to theDict’s writeToURL:(folderURL’s URLByAppendingPathComponent:aName) atomically:true
  
  
return aRes as boolean
  
end saveRecordToFolAsPlist

★Click Here to Open This Script 

2013/03/10 Safariで指定可能なユーザーエージェントのリストを返す

Safariでプリセットで選択可能なユーザーエージェントを取得するAppleScriptです。OS X 10.8.2+Safari 6.0.3上で動作確認しています。

GUI Scriptingを用いているので、あらかじめシステム環境設定の「アクセシビリティ」で「補助装置にアクセスできるようにする」のチェックを入れておいてください。

safari0.png

Safariの「環境設定」>「詳細」で、「メニューバーに”開発”メニューを表示」の項目のチェックをオンにしておくと、メニューバーに「開発」の項目が表示されるようになります(Macユーザーの一般常識)。

この「開発」メニューの「ユーザーエージェント」の下に、Safariがあらかじめ用意しているユーザーエージェントの選択メニューが表示されます。この項目を適宜選択すると、指定ユーザーエージェント名でWebサーバーにアクセスするようになります。

safari1.png

この、Safariがメニューから指定可能なユーザーエージェントの一覧を取得します。

safari2.png

Safariではプリセットのユーザーエージェント名以外にも、「その他」から適宜指定可能なので、別にプリセットのユーザーエージェント名にこだわる必要はありません。

スクリプト名:Safariで指定可能なユーザーエージェントのリストを返す

–Safariでメニューから指定可能なUser Agentのリストを取得する
set uaList to retEnableUserAgentFromSafari() of me
–> {”Safari 6.0.3 ― Mac”, “Safari 5.1.7 ― Windows”, “Safari iOS 5.1 ― iPhone”, “Safari iOS 5.1 ― iPod touch”, “Safari iOS 5.1 ― iPad”, “Internet Explorer 9.0″, “Internet Explorer 8.0″, “Internet Explorer 7.0″, “Google Chrome 19.0 ― Mac”, “Google Chrome 19.0 ― Windows”, “Firefox 11.0 ― Mac”, “Firefox 11.0 ― Windows”, “Opera 11.62 ― Mac”, “Opera 11.62 ― Windows”}

–Safariで指定可能なユーザーエージェントのリストを返す
on retEnableUserAgentFromSafari()
  
  
–除外リスト(This list is for Japanese environment. This depends on each localized menu item string)
  
set exList to {“デフォルト(自動選択)”, “その他…”}
  
  
–activate application “Safari”
  
tell application “System Events”
    tell process “Safari”
      
      
–開発メニューのイネーブルチェック
      
tell menu bar 1
        set tList to title of every menu bar item
      end tell
      
      
if “開発” is not in tList then –(This String is for Japanese environment. This depends on each localized menu item string)
        display dialog “開発メニューがオンになっていません” buttons {“OK”} default button 1 with icon 1
        
return
      end if
      
      
–「開発」>「ユーザーエージェント」
      
tell menu 1 of menu item 2 of menu 1 of menu bar item 8 of menu bar 1
        set t2List to title of every menu item whose title is not “” –セパレーター以外のメニュー項目を取得
      end tell
      
    end tell
  end tell
  
  
set t3List to {}
  
repeat with i in t2List
    set j to contents of i
    
if j is not in exList then
      set the end of t3List to j
    end if
  end repeat
  
  
return t3List
  
end retEnableUserAgentFromSafari

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

2013/02/21 指定フォルダ中のファイル名が拡張子を外すと衝突するかどうかチェック

指定フォルダ中のファイルから拡張子を外した状態で名称が衝突するかどうかチェックするAppleScriptです。

あるフォルダ(InDesign書類のパッケージ化したフォルダ中にあるLinksフォルダ)の中に入っている雑多な画像ファイル(EPSとかPhotoshopとかIllustrator書類とかいろいろ)をすべてJPEG画像に書き出すプログラムをさくっと作った……まではよかったのですが、処理を行うとエラー表示も出ないのに処理前の画像数と処理後の画像数が合わないという状態に。

さっそく、フォルダ内のファイル名一覧を取得して、Excelのシート上にペーストして見比べて……………「ファイル名が同じで拡張子だけが違う」というやっかいなファイルが存在していることに(自分以外の人が)気付きました。

folder1.png

前述の「処理前のファイル数と処理後のファイル数が合わない」という問題は、拡張子の部分以外のファイル名が重複しているファイルの両方ともが順次JPEGに変換されて、上書きされて先に処理したファイルが消されたために生じたものでした。

対処方法は「処理前に検出する」ことと「ファイルを書き込むときに迂回処理を行う」ことの2つ。

……簡単そうなのは前者だったので、作ってみたら割とすごいことに。すでにどこかで使ったルーチンのオンパレードですが、それなりの長さに。

# ファイル名の衝突迂回プログラムは実は作ってあったのですが……その存在を忘れていました。OS側がやっているのと同じく、存在確認しつつファイル名に子番号を(インクリメンタルに)付けていくというものです

指定フォルダ内のファイル名一覧を取得し、全ファイル名から拡張子を外した名称リストを作成。名称リスト内で重複がないかどうかをチェックします。

重複が存在していた場合には、

folder2.png

のように、どのファイルの組み合わせに問題があるかを一覧表示して終了します。

スクリプト名:指定フォルダ中のファイル名が拡張子を外すと衝突するかどうかチェック
set aFol to choose folder with prompt “フォルダを選択してください。”

set aFolStr to aFol as string

tell application “Finder”
  tell folder aFolStr
    set aList to name of every file
  end tell
end tell

–指定フォルダ内のファイル名の(拡張子を外した部分の)衝突判定を行う
set aRes to chkFileNameDuplicatesWithoutExtension(aList) of detectDupKit
if aRes is not equal to {} then
  
  
set dupList to {}
  
  
repeat with i in aRes
    set j to contents of i
    
tell application “Finder”
      tell folder aFolStr
        set aList to (name of every file whose name begins with j)
      end tell
    end tell
    
    
set the end of dupList to aList
    
  end repeat
  
  
set dup2List to FlattenList(dupList) of me
  
set firstItem to contents of first item of dup2List
  
choose from list dup2List default items {firstItem} with prompt “指定フォルダ中の以下のファイルは拡張子以外が同じため、後の処理でファイル名の衝突が起こります。
  
あらかじめどちらか一方を削除するか、片方の名前を変更するなどしておいてください。” with title “ファイル名の重複エラー”
  
  
return –終了
  
end if

script detectDupKit
  
  
–ファイル名リストをリスト型変数で渡すと、拡張子を外した名前で重複検出を行う
  
on chkFileNameDuplicatesWithoutExtension(aList)
    –拡張子を外す  
    
set aNList to {}
    
repeat with i in aList
      set j to contents of i
      
set the end of aNList to retFileNameWithoutExt(j) of me
    end repeat
    
    
–重複検出
    
set dupList to detectDuplicates(aNList) of me
    
if dupList = {} then return {}
    
    
–重複情報を単純なリストにして返す
    
set d2List to {}
    
repeat with i in dupList
      set j to contents of first item of i
      
set the end of d2List to j
    end repeat
    
    
return d2List
    
  end chkFileNameDuplicatesWithoutExtension
  
  
on removeDuplicates(aList)
    set newList to {}
    
repeat with i from 1 to (length of aList)
      set anItem to item 1 of aList
      
set aList to rest of aList
      
if {anItem} is not in aList then set end of newList to anItem
    end repeat
    
return newList
  end removeDuplicates
  
  
–ファイル名から拡張子を外す
  
on retFileNameWithoutExt(fileNameStr)
    set fLen to length of fileNameStr
    
set revText to (reverse of (characters of fileNameStr)) as string –逆順テキストを作成
    
set anOffset to offset of “.” in revText
    
set fRes to text 1 thru (fLen - anOffset) of fileNameStr
    
return fRes
  end retFileNameWithoutExt
  
end script

–与えられたリストの中で重複しているものをピックアップ。アルファベットの大文字小文字の違いを無視
on detectDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set tmpList to {}
    
    
–あえてアルファベットの大文字小文字の違いを無視(というよりも、同一視)
    
ignoring case
      set anItem to item 1 of aList
      
set aList to rest of aList
      
if {anItem} is in aList then set end of tmpList to anItem
      
repeat with j in aList
        set jj to contents of j
        
if jj = anItem then
          set end of tmpList to jj
          
exit repeat –重複は基本的にABC-abcのように1対のみという前提。フォルダ名の衝突回避が目的なので
        end if
      end repeat
    end ignoring
    
    
if tmpList is not equal to {} then
      set the end of newList to tmpList
    end if
  end repeat
  
return newList
end detectDuplicates

–By Paul Berkowitz
–2009年1月27日 2:24:08:JST
–Re: Flattening Nested Lists
on FlattenList(aList)
  set oldDelims to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {“????”}
  
set aString to aList as text
  
set aList to text items of aString
  
set AppleScript’s text item delimiters to oldDelims
  
return aList
end FlattenList

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

2013/02/02 USBメモリやネットワーク上のサーバーなどをアンマウント

USBメモリやネットワーク上のサーバーなどをアンマウントするAppleScriptです。

ファイルやディスクなどFinder上のオブジェクトに対してさまざまな操作をAppleScriptから行おうとしたら、「Finderというアプリケーション」に対して命令を出すことになります。

となると、当然のことながら「Finder」のAppleScript用語辞書を調べる必要があります。

finder1.png

AppleScriptエディタのツールバーで、「包含階層を表示」ボタンをクリック。

finder2.png

Applicationの下にdiskオブジェクトがあることが分ります。

finder3.png

とりあえず、ネットワーク上のサーバーやUSBメモリーを実際にマウントしてみて、プロパティを取得するなどアプリケーションと「対話」して調べてみましょう。ここでは、「3bai」という名前のUSBメモリーをマウント、そのプロパティを取得。

スクリプト名:3baiのプロパティを取得
tell application “Finder”
  properties of disk “3bai”
end tell

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

結果はこのようなものが得られました。

finder4.png

「ejectable」属性を見て、USBメモリーは判断できそうです。また、ネットワーク上のドライブ(ファイルサーバーとか、NASとか)であれば、「local volume」属性を見れば判定は可能です。

なので、おもむろに………

スクリプト名:USBメモリやネットワーク上のサーバーなどをアンマウント
tell application “Finder”
  eject (every disk whose ejectable is true) –外付けHDD, USBメモリー
  
eject (every disk whose local volume is false) –ネットワーク上のドライブ
end tell

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

こんな感じで、とくにエラートラップやタイムアウト時間の延長などは行っていませんが、素直に書くことができました。

ただし、これら外付けドライブ上のファイルをオープンしているアプリケーションが存在している場合に、ドライブのアンマウントが中断されてしまいますので、そのあたりは別の対策が必要になると思います。

2013/01/20 システムにインストールされているICC Profileのうち、指定キーワードに該当する名前を持つものでバージョン番号が最新のものを返す v2

システムにインストールされているICC Profileのうち、指定キーワードに該当する名前を持つもので、バージョン番号が最新のものをピックアップするAppleScriptです。

…………最初のものから、50行ぐらい短くなっています。

ICC Profileは/Library/ColorSync/Profilesとか、~/Library/ColorSync/Profilesなど複数箇所にインストール可能で、Mac OS Xが標準で用意しているものと、Adobeの各アプリがてんでバラバラにインストールしてお互いに互換性のないものなどが無造作に入っています。

Mac OS X 10.6でColorSync Scriptingが廃止になったときに、ICC Profileの管理機能はImage Eventsに移管されました(少し前からそうだったか、、、)。このImage Eventsに対してICC Profileの所在を問い合わせると、複数のインストール先にインストールされた同一名称のプロファイルが取得できたりするので、ユニーク化処理を行って重複分の消し込みを行っています。

スクリプト名:システムにインストールされているICC Profileのうち、指定キーワードに該当する名前を持つものでバージョン番号が最新のものを返す v2
–システムにインストールされているICC Profileのうち、指定キーワードに該当する名前を持つものでバージョン番号が最新のものを返す

set nList to getAProfile_(“Piyomaru Piyopiyo”)
set b to retLatestVersionICCprofileNameFromList_delExt_(nList)
–> “Piyomaru Piyopiyo V5.0.icc”

–キーワードを含む名称を持つICC Profileを取得する
on getAProfile_(aKeyword)
  
  
set unique_profile_names to {}
  
  
tell application “Image Events”
    
    
set all_profile_names to name of every profile whose name contains aKeyword
    
    
repeat with i from 1 to (count all_profile_names)
      set cur_profile_name to item i of all_profile_names
      
      
if cur_profile_name is not in unique_profile_names then
        set end of unique_profile_names to cur_profile_name
      end if
      
    end repeat
    
  end tell
  
  
return unique_profile_names
  
end getAProfile_

–与えられたICC Profileの名称リストからもっともバージョンの新しい(大きい)数字を持つものを返す
on retLatestVersionICCprofileNameFromList_delExt_(a)
  
  
–取り出した数値部分だけをキーにして降順ソート(大きな数→小さな数)
  
considering numeric strings
    set resList to shellSortDecending(a)
  end considering
  
  
return item 1 of resList
  
end retLatestVersionICCprofileNameFromList_delExt_

–シェルソートでリストを昇順ソート(入れ子ではないリスト)
on shellSortAscending(a)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item (j - h + 1) of a) > v)
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortAscending

–シェルソートでリストを降順ソート(入れ子ではないリスト)
on shellSortDecending(a)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item (j - h + 1) of a) < v)
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortDecending

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

2013/01/16 指定プロセスの死活判定 v2

指定プロセス(アプリケーション)がハングアップしているかどうかをUNIX的な見地と、AppleScript的な見地から判定するAppleScriptです。

以前掲載したバージョンでは、shellのpsコマンドだけで「クラッシュした」状態を検出していましたが、それだけでは不十分という話を書いていました。

そこで、2フェーズに分けて……

 フェーズ1:psコマンドでゾンビプロセスの確認
 フェーズ2:AppleScriptから指定アプリに名称確認

という確認をするようにしてみました。

これだけ丁寧にきめ細かく確認を行えば、たいていのクラッシュやハングアップ状態は検出できることでしょう(あいにく、Mac OS X上ではそんなに頻繁にそういう状態に遭遇していないので確認が難しいのですが)。ぜひ、いろいろなケースで試してみたいところです。

スクリプト名:指定プロセスの死活判定(Bundle IDで判定) v2
set aRes to detectAppAliveByID_(“com.apple.itunes”)
–> true (起動中、ゾンビ化していない)
–> false(起動していない)
–> “killed”(ゾンビ化していたので、killした)

–指定プロセスの死活判定(Bundle IDで判定)
on detectAppAliveByID_(aProcBundleID)
  set aProcBundleID to aProcBundleID as string
  
  
–Phase 1 psコマンド経由で状態を確認してみる
  
set aRes to false
  
try
    tell application “System Events”
      set aList to bundle identifier of every process
      
if aProcBundleID is in aList then
        
        
set tmpList to name of every process whose bundle identifier = aProcBundleID
        
set aProcName to contents of first item of tmpList
        
        
set aRes to true
        
tell process aProcName
          set processID to unix id –プロセスIDを取得
        end tell
        
        
set processID to processID as string
        
        
–指定プロセスがゾンビプロセス化しているかどうかを判定
        
set procState to (words of (do shell script “/bin/ps “ & processID & ” | cut -d ‘ ‘ -f 6″)) as string
        
        
if procState contains “Z” then
          –ゾンビプロセスを殺す
          
do shell script “/bin/kill -9 “ & processID
          
return “killed”
        end if
      else
        –指定したBundle IDのプロセスが存在しない場合falseでリターン
        
return false
      end if
    end tell
  on error
    –異常発生時にはさっさとリターン
    
return false
  end try
  
  
  
–Phase 2 指定アプリに直接コンタクトして反応を見る
  
try
    with timeout of 3 seconds
      tell application id aProcBundleID
        set aTmpvar to name –これができない(Scriptableな)アプリはほとんどないだろう(多分)
      end tell
    end timeout
  on error
    –名称を取得できなかった場合にはコケていると見なしてプロセスを殺す
    
do shell script “/bin/kill -9 “ & processID
    
return “killed”
    
  end try
  
  
  
return aRes
end detectAppAliveByID_

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

スクリプト名:指定プロセスの死活判定(アプリ名で判定) v2
set psRes to detectAppAliveByName_(“Safari”)
–> true (起動中、ゾンビ化していない)
–> false(起動していない)
–> “killed”(ゾンビ化していたので、killした)

–指定プロセスの死活判定(アプリ名で判定)
on detectAppAliveByName_(aProcName)
  set aProcName to aProcName as string
  
  
–Phase 1 psコマンド経由で状態を確認してみる
  
set aRes to false
  
try
    tell application “System Events”
      set aList to name of every process
      
if aProcName is in aList then
        set aRes to true
        
tell process aProcName
          set processID to unix id –プロセスIDを取得
        end tell
        
        
set processID to processID as string
        
        
–指定プロセスがゾンビプロセス化しているかどうかを判定
        
set procState to (words of (do shell script “/bin/ps “ & processID & ” | cut -d ‘ ‘ -f 6″)) as string
        
        
if procState contains “Z” then
          –ゾンビプロセスを殺す
          
do shell script “/bin/kill -9 “ & processID
          
return “killed”
        end if
      else
        –指定した名称のプロセスが存在しない場合falseでリターン
        
return false
      end if
    end tell
  on error
    –異常発生時にはさっさとリターン
    
return false
  end try
  
  
  
–Phase 2 指定アプリに直接コンタクトして反応を見る
  
try
    with timeout of 3 seconds
      tell application aProcName
        set aTmpvar to name –これができない(Scriptableな)アプリはほとんどないだろう(多分)
      end tell
    end timeout
  on error
    –名称を取得できなかった場合にはコケていると見なしてプロセスを殺す
    
do shell script “/bin/kill -9 “ & processID
    
return “killed”
    
  end try
  
  
return aRes
end detectAppAliveByName_

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

2013/01/12 最前面のアプリケーションをいったん終了させてから起動

最前面のアプリケーションをいったん終了させて、ふたたび起動させるAppleScriptです。

これは、コケかけているような怪しい状態のアプリケーションを対象としているものではなく、明確に動いているものを操作するためのものです。

画面の解像度を(狭い方に)変更したときに、フローティングパレットやフローティングウィンドウが隠れてしまうことがあります。その現象を解決するためにいったん終了させてふたたび起動するものです。とくに、アクティビティモニタのCPUの負荷メーターが見えなくなってしまう問題に対処するために作りました。

Script Menuに入れて呼び出して使っています。

スクリプト名:最前面のアプリケーションをいったん終了させてから起動
tell application “System Events”
  set aProc to every process whose frontmost is true
  
set aaProc to contents of first item of aProc
  
  
set aProp to properties of aaProc
  
  
set aBundleID to bundle identifier of aProp
  
end tell

tell application id aBundleID to quit

delay 1

tell application id aBundleID to launch

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

2013/01/12 指定プロセスの死活判定

指定プロセス(アプリケーション)がハングアップしているかどうかをUNIX的な見地から判定するAppleScriptです。

アプリ名称で指定して調べるものと、バンドルIDで指定して調べるものを用意してみました。

System Eventsで指定プロセス(アプリケーション)のプロパティを調べると、unix id(process id)を取得できます。このunix idで指定したプロセスの状態をpsコマンドで取得し、ゾンビプロセスになっているかどうかを判定します。

ただ……psコマンドで調べてゾンビプロセスに「なっていない」ものでも、GUI側から操作を試みても7色のビーチボールが回りっぱなしだったり、AppleScriptからの命令を受け付けないといった状態になっていることもままあります。

プロセス(アプリケーション)の死活判定はなかなか奥深いものがあります。実際には、psコマンドで調べるだけでなく、テストデータをオープンさせてみるとか、アプリケーションの名称を取得してみるとか、実行しても意味はないものの結果が得られるかどうかを調べてみるとよいでしょう。

スクリプト名:指定プロセスの死活判定(アプリ名で判定)
set psRes to detectAppAliveByName_(“Safari”)
–> true (起動中、ゾンビ化していない)
–> false(起動していない)
–> “killed”(ゾンビ化していたので、killした)

–指定プロセスの死活判定(アプリ名で判定)
on detectAppAliveByName_(aProcName)
  set aProcName to aProcName as string
  
  
set aRes to false
  
try
    tell application “System Events”
      set aList to name of every process
      
if aProcName is in aList then
        set aRes to true
        
tell process aProcName
          set processID to unix id –プロセスIDを取得
        end tell
        
        
set processID to processID as string
        
        
–指定プロセスがゾンビプロセス化しているかどうかを判定
        
set procState to (words of (do shell script “/bin/ps “ & processID & ” | cut -d ‘ ‘ -f 6″)) as string
        
        
if procState contains “Z” then
          –ゾンビプロセスを殺す
          
do shell script “/bin/kill -9 “ & processID
          
set aRes to “killed”
        end if
      else
        set aRes to false
      end if
    end tell
  on error
    set aRes to false
  end try
  
  
return aRes
end detectAppAliveByName_

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

スクリプト名:指定プロセスの死活判定(Bundle IDで判定)
set aRes to detectAppAliveByID_(“com.apple.safari”)
–> true (起動中、ゾンビ化していない)
–> false(起動していない)
–> “killed”(ゾンビ化していたので、killした)

–指定プロセスの死活判定(Bundle IDで判定)
on detectAppAliveByID_(aProcBundleID)
  set aProcBundleID to aProcBundleID as string
  
  
set aRes to false
  
try
    tell application “System Events”
      set aList to bundle identifier of every process
      
if aProcBundleID is in aList then
        
        
set tmpList to name of every process whose bundle identifier = aProcBundleID
        
set aProcName to contents of first item of tmpList
        
        
set aRes to true
        
tell process aProcName
          set processID to unix id –プロセスIDを取得
        end tell
        
        
set processID to processID as string
        
        
–指定プロセスがゾンビプロセス化しているかどうかを判定
        
set procState to (words of (do shell script “/bin/ps “ & processID & ” | cut -d ‘ ‘ -f 6″)) as string
        
        
if procState contains “Z” then
          –ゾンビプロセスを殺す
          
do shell script “/bin/kill -9 “ & processID
          
set aRes to “killed”
        end if
      else
        set aRes to false
      end if
    end tell
  on error
    set aRes to false
  end try
  
  
return aRes
end detectAppAliveByID_

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

2012/09/30 Mailで選択中のmessageの親フォルダのフルパスを文字で取得する

OS X 10.8標準装備のMail.app v6で、選択中のメッセージに対して、親フォルダのフルパスを文字で取得するAppleScript+動作確認用の呼び出し側Scriptです。

動作確認用に、「メール」アプリ上で選択中のメッセージが入っているフォルダのフルパスを取得し、そのフォルダ中に指定文字列を含むフォルダが存在するかどうかを確認するAppleScriptを書いてあります。ただし、あくまで動作確認用であり、本体部分は、

–与えられたmailboxのフルパスをテキストで返す
on getFullPath(mBox)

および、

–親フォルダを取得
on getParent(aMailBox)

の2つのサブルーチンです。getFullPathは10.8およびそれ以前のMac OS X(10.4あたりまで)に対応していますが、getParentは10.8にしか対応していませんね。本来、getParentもバージョン判定を行う必要があります。

mail_folder.png

スクリプト名:Mailで選択中のmessageの親フォルダのフルパスを文字で取得する
tell application “Mail”
  –先にSelectionを取得してみる
  
set aSelection to selection
  
if aSelection = {} then
    display dialog “Mail.app上でメールが選択されていません。” buttons {“OK”} default button 1 with icon note with title “ERROR”
    
return
  end if
  
  
–選択中のメールボックスを取得
  
tell message viewer 1
    set mbList to selected mailboxes
  end tell
  
  
–Selectionから情報を取得する処理
  
set anItem to item 1 of aSelection
  
set anItemProp to properties of anItem
  
set aStorage to mailbox of anItemProp
  
set aSubject to subject of anItemProp
  
  
  
set aRes to text returned of (display dialog “存在確認するフォルダ名称は?” default answer aSubject)
  
  
–すでに同一名称でフォルダが作成されていないか確認
  
tell aStorage
    ignoring case, diacriticals, hyphens, punctuation and white space
      set fList to every mailbox whose name contains aRes
    end ignoring
  end tell
  
  
set tmpFolObj to first item of fList
  
set tmpRes to getFullPath(tmpFolObj) of me
  
  
return tmpRes
  
end tell

–与えられたmailboxのフルパスをテキストで返す
on getFullPath(mBox)
  tell application “Mail”
    set aVer to version as number
    
    
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 aVer 6 then
        –Mountain Lion搭載のMail 6.0以降の場合
        
if aClass is not equal to container then – ここを変更
          exit repeat
        end if
      else
        –それ以前の場合?
        
if aClass is not equal to mailbox then – ここを変更
          exit repeat
        end if
      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

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

2012/08/22 指定フォルダ内のPDFの空白ページ以外を個別に取り出して別フォルダに保存 v3

指定フォルダ内のPDFの空白ページ「以外」を個別に取り出して、別フォルダに保存するAppleScriptです。

HTMLコンテンツを大量にプリンタで印刷するのに、HTMLレンダラー「wkpdf」を用いて個別のPDFに書き出し、これを連結してまとめてプリンタに送ろうとしたときに必要で作ったものです。

wkpdfでレンダリングしたときに余白ができて、内容が2ページ目まであふれていないのに空白の2ページ目が作られるケースがありました。これに対策すべく……

(1)PDFのページ数を数える。1ページのPDFなら、出力先フォルダにそのまま(ページ数を振りつつ)コピー

(2)2ページ以上のPDFなら、skimでPDFをオープンして、各ページに存在する文字情報を取得。2ページ目以降に文字情報が存在していない場合には、グラフィックが存在している可能性があるため、個別にPDFに書き出して、Photoshopでオープンし画像のヒストグラムを取得。画像のヒストグラムにドットの反応がなければ、「真っ白なページだった」=「空白ページ」とみなして破棄。ドットの反応があれば、出力先フォルダに(ページ数を振りつつ)コピー

という動作を行います。

この前のバージョンでは、2ページ目への文字あふれしか検出できていなかったのですが、改良してグラフィック要素があふれた場合でも対応できるようにしました。

実戦投入して、便利に使えています。

なお、おおきなおともだちの皆様にはお分かりと思いますが、Photoshop CS3以降でなくともPhotoshop Elements(お安い)でも代用できます。

スクリプト名:指定フォルダ内のPDFの空白ページ以外を個別に取り出して別フォルダに保存 v3
set inFol to choose folder with prompt “処理対象のPDFが入っているフォルダを指定”

set dtPath to choose folder with prompt “出力先フォルダを選択”
set dtPathStr to dtPath as string

tell application “Finder”
  tell folder inFol
    set aFileList to (every file whose name ends with “.pdf”) as alias list
  end tell
end tell

–それぞれのPDFの1ページ目のみ抽出
repeat with i in aFileList
  set j to contents of i
  
set pNum to pdfCount(j) of me
  
if pNum = 1 then
    –ファイルをコピー、ファイルのリネーム?
    
tell application “Finder”
      set fRes to duplicate j to dtPath
      
set tmpName to name of fRes
      
      
set bName to retFileNameWithoutExt(tmpName) of me
      
set cName to bName & “_1.pdf”
      
      
set name of fRes to cName
    end tell
    
    
  else if pNum > 1 then
    –1ページ目以外のページを、空白でないかどうかチェックする
    
set outList to {1}
    
    
repeat with ii from pNum to 2 by -1
      set aRes to detectBlankPageFromPDF(j, ii) of me
      
if aRes = false then
        set the end of outList to ii
      end if
    end repeat
    
    
    
–空白でないページのみ切り出して出力
    
repeat with ii in outList –出力対象ページ番号が入ったリスト
      set jj to contents of ii
      
set aRes to trimPDFbyPage(j, dtPathStr, jj) of me –エラー時にはfalseが返る
      
    end repeat
    
    
  end if
end repeat

–PDFの指定ページのみトリミング
on trimPDFbyPage(aFile, outFol, pageNum)
  tell application “Skim”
    close every document
    
    
open aFile
    
    
tell document 1
      set cCount to count every page
      
if cCount < pageNum then return false
    end tell
    
    
tell document 1
      set aProp to properties
      
set aRes to info of aProp
      
set aName to file name of aRes
      
set bName to retFileNameWithoutExt(aName) of me
      
      
set cName to bName & “_” & (pageNum as string) & “.pdf”
      
      
tell page pageNum
        set bList to bounds
        
set anImage to grab for bList –type “PDF”
      end tell
    end tell
    
    
set target_file to outFol & cName
    
write_to_file(anImage, target_file, false) of me
    
    
close every document
    
  end tell
end trimPDFbyPage

–ファイル名から拡張子を外す
on retFileNameWithoutExt(fileNameStr)
  set fLen to length of fileNameStr
  
set revText to (reverse of (characters of fileNameStr)) as string –逆順テキストを作成
  
set anOffset to offset of “.” in revText
  
set fRes to text 1 thru (fLen - anOffset) of fileNameStr
  
return fRes
end retFileNameWithoutExt

–指定PDFのページ数をかぞえる
on pdfCount(aPDF)
  return ((do shell script (“ruby -e \”require ‘osx/cocoa’;include OSX;require_framework ‘Quartz’;print PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(” & (quoted form of POSIX path of aPDF) & “)).pageCount\”")) as number)
end pdfCount

on makeFN(aNum, aDigit)
  set aText to “00000000000″ & (aNum as text)
  
set aLen to length of aText
  
set aRes to text (aLen - aDigit) thru -1 of aText
  
return aRes
end makeFN

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_file

–指定PDFの指定ページが空白(真っ白)かどうか判定する
on detectBlankPageFromPDF(aFile, targPage)
  tell application “Skim”
    close every document
    
    
open aFile
    
    
tell document 1
      set cCount to count every page
      
set cDigit to length of (cCount as string)
      
      
set docName to name
      
      
tell page targPage
        set bList to bounds
      end tell
      
      
if cCount > 1 then
        
        
–指定ページに文字が存在するかチェック
        
tell page targPage
          set aProp to properties
          
set aText to first item of text of aProp
        end tell
        
        
if aText is not equal to “” then
          return false
        end if
      end if
      
      
tell page targPage
        set anImage to grab for bList –type “PDF”
      end tell
    end tell
    
    
close every document
    
  end tell
  
  
–指定ページ内容をテンポラリフォルダに書き出す
  
set outFol to (path to temporary items from system domain) as string
  
set docName to (do shell script “/usr/bin/uuidgen”) & “.pdf”
  
  
set target_file to outFol & docName
  
write_to_file(anImage, target_file, false) of me
  
  
do shell script “sync” –キャッシュされたままディスクに書かれていないことがあるので実行
  
  
  
–書き出したPDFをPhotoshopでオープンしてヒストグラムから描画物の存在を検出
  
–Photoshop Elementsでも代用可
  
tell application “Adobe Photoshop CS3″
    close every document saving no
    
open file target_file
    
tell current document
      set aList to histogram
    end tell
    
close every document saving no
  end tell
  
  
–テンポラリPDFを削除する
  
do shell script “/bin/rm -f “ & (quoted form of POSIX path of target_file)
  
  
set aRes to detectWhiteFromList(aList) of me
  
  
return aRes –空白だとtrueを、そうでない場合にはfalseを返す
  
end detectBlankPageFromPDF

–与えられたPhotoshopヒストグラム(256段階の明るさのピクセル数)から、その内容が真っ白かどうかを検出
on detectWhiteFromList(aList)
  set aLen to length of aList
  
  
set zeroF to true
  
repeat with i from 1 to (aLen - 1) –真っ白かどうかを検知している
    if item i of aList is not equal to 0 then
      set zeroF to false
      
exit repeat
    end if
  end repeat
  
  
return zeroF
  
end detectWhiteFromList

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

2012/04/29 Terminalで全Window中の全Tabのうち、指定タイトルのものをクローズする

Terminal.appの全Window内のTabのうち、指定タイトルのものをクローズするAppleScriptです。

Mac OS X 10.7上のTerminalの各ウィンドウには、任意のタイトルを指定できるようになっています。Mac OS X 10.6上では、カスタマイズしたタイトルについてはAppleScriptからは参照するだけでした。

term00.jpg

スクリプト名:teminal.appでtabを操作する
tell application “Terminal”
  tell window 1
    tell tab 1
      set custom title to “ぴよまる”
    end tell
    
    
tell tab 2
      set custom title to “ぴよぴよ”
    end tell
    
  end tell
end tell

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

term01.jpg

term3.jpg

Tabのクローズについては、Window内のTabを指定してclose命令を実行してみても、クローズはできない状況です。GUI Scriptingで行うのもアレなので(最前面に持ってこないと実行できないというのは、ちょっとめんどう)、別の方法でクローズを実現することに。

Terminal.appの環境設定で、シェル終了後にWindowをどうするか指定することができます。

term1.jpg

term4.jpg

「ウィンドウを閉じる」「シェルが正常終了した場合には閉じる」「ウィンドウを閉じない」の3つから選択可能。

term2.jpg

これを「ウィンドウを閉じる」に設定しておき、指定Tabに「exit」コマンド(シェルコマンド)を送ることで、指定Tabをクローズできるようになります。

まあそんなわけで、Tabのカスタム名称を文字列で指定して該当するTabをすべてクローズできるようになったわけですが、これがどの程度有用かは…………ちょっと分かりません。

スクリプト名:Terminalで全Window中の全Tabのうち、指定タイトルのものをクローズする
set targTabTitle to “ぴよまる”

tell application “Terminal”
  set tabRef to a reference to (every tab of every window whose custom title is equal to targTabTitle)
  
  
repeat with i in tabRef
    do script “exit” in i
  end repeat
  
end tell

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

2012/03/24 インストールされているアプリのAS辞書を書き出すv2

2012/03/12 Find same file name with different extension in same directory

This is a simple AppleScript to find same file name with different extension in same directory.

Sample Data:
finder1.jpg

Output Data:
–> {{”D120_a.jpg”, “D120_a.png”}, {”D120_c.gif”, “D120_c.jpg”, “D120_c.png”, “D120_c.txt”}}

スクリプト名:find same file name with different extension in same directory
–Speed up technic
script aRef
  property aList : {} –every file name of user selected folder ( work)
  
property bList : {} – every file name list (without duplicates) (work)
  
property cList : {} –duplicated file name list (work)
  
property dList : {} –output duplicated file names with different extensions
end script

–Initialize variables
set aList of aRef to {}
set bList of aRef to {}
set cList of aRef to {}
set dList of aRef to {}

set a to choose folder with prompt “Choose check Folder”

tell application “Finder”
  tell folder a
    set aList of aRef to name of every file
  end tell
end tell

–make pure file name list(without extension) to cList
repeat with i in aList of aRef
  set j to contents of i
  
set jj to retFileNameWithoutExt(j) of me
  
  
if jj is in bList of aRef then
    –duplicated file name & does not exist in cList
    
if jj is not in cList of aRef then
      set the end of cList of aRef to jj
    end if
    
    
set tmpName to jj & “.”
    
    
tell application “Finder”
      tell folder a
        –set dList of aRef to dList of aRef & (name of every file whose name starts with tmpName)
        
set the end of dList of aRef to (name of every file whose name starts with tmpName)
      end tell
    end tell
    
  end if
  
  
set the end of bList of aRef to jj
end repeat

–Remove duplicates from List
set aRes to removeDuplicates(dList of aRef) of me

–> {{”D120_a.jpg”, “D120_a.png”}, {”D120_c.gif”, “D120_c.jpg”, “D120_c.png”, “D120_c.txt”}}

–Remove Duplicated Item from List
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–delete extension from file name
on retFileNameWithoutExt(fileNameStr)
  set fLen to length of fileNameStr
  
set revText to (reverse of (characters of fileNameStr)) as string –make reversed string
  
set anOffset to offset of “.” in revText
  
set fRes to text 1 thru (fLen - anOffset) of fileNameStr
  
return fRes
end retFileNameWithoutExt

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

2012/03/07 InDesign CS3で指定のオプジェクトを、JPEG書き出ししてiPhoneへ

InDesign CS3上で指定したオブジェクトをJPEG書き出ししてiPhoneに転送するAppleScriptです。

ことのはじまりは、目下開発中のiPhoneアプリ(私はプロジェクト管理やら仕様書書きやらを)で、Welcome画面の表示を行うのに「文字詰めや行送りをいろいろ指定できないと!」という話になったこと。

……そもそも、iPhoneの文字表示部品に、そんな気の利いたものはありません。そこで、InDesignやらIllustrator上で気の済むまで行間や文字間にこだわっていただいて、それをそのまま「画像」として書き出して表示しようではないか、と。

ただし、その指定内容をすぐにiPhoneの実機で見たいという話に。

その場で10分もかけずにInDesign書類上の指定レイヤー(「コンテンツ」)上に存在する指定名称(スクリプトラベル「screen」)のグループをJPEG書き出しして、書き出したJPEG書類をiPhotoにインポートして、iTunesに命令して接続しているすべてのiOSデバイスにシンクロを行うようAppleScriptで指令を出すようにしてみました。

できた瞬間はかなりガッツポーズでしたが、冷静に考えるとInDesignから書き出されるJPEG画像のクオリティが「残念なレベル」です。また、シンクロが終わるまでにちょっと時間がかかるので、実用性がいまひとつ。

結局、Good Reader for iPhoneをインストールして、iTunes経由でGood Readerに対してInDesignから書き出したPDFを渡す、という方法に落ち着きました。本Scriptは何らかの「可能性」を感じさせてくれはしたのですが、結局おクラ入りに。

スクリプト名:InDesign CS3で指定のオプジェクトを、JPEG書き出ししてiPhoneへ
–v1 InDesign CS3から直接JPEG書き出し。画像クオリティが低くて難あり

set dtPath to (path to desktop) as string
set fName to do shell script “date +%Y%m%d%H%M%S”

set fullPath to dtPath & fName & “.jpg”
set fullPathPOSIX to POSIX path of fullPath

tell application “Adobe InDesign CS3″
  tell document 1
    tell layer “コンテンツ”
      set aList to every group whose label is equal to “screen”
    end tell
    
    
set expItem to contents of first item of aList
    
export expItem format JPG to fullPath without with grids
    
  end tell
end tell

–iPhotoに書き出したJPEGファイルをインポートする
tell application “iPhoto”
  import from fullPathPOSIX
end tell

–アップデート可能なiOSデバイスをアップデートする
tell application “iTunes”
  repeat with i in sources
    if (kind of i is iPod) then update i
  end repeat
end tell

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

2012/02/07 iTunesのMobileアプリをコピーしてすべて展開する

このAppleScriptは、iTunesに入っているiPhone/iPadのアプリを指定フォルダにコピーして、アーカイブを展開して、さらにアプリケーションバンドルを「ただのフォルダ」に変えるものです。

iTunesをコントロールして、iOSアプリの情報を取得しようとしても……iTunes上でアプリの情報を取得するための機能は(iTunesのAppleScript用語辞書には)一切用意されていません(selectionすら取得できないのには意図的なものを感じる)。でも、アプリの情報にアクセスするのに、iTunesの力は必要ありません。ファイル・アクセスだけで十分です。

iosap1.jpg

iOSのアプリは拡張子「.ipa」のファイルであり、このファイルはZipアーカイブの拡張子を変えただけのものなので、簡単に展開できます。

iosap2.jpg

展開すると「Payload」フォルダ内にiOSアプリケーションが展開されます。さらに、このiOSアプリの拡張子「.app」を外します。

iosap3.jpg

iOSアプリのアプリケーションバンドル中に入っている各種データをSpotlightでキーワード検索して探し出すことが可能になります。

iosap4.jpg

一度走らせれば一括処理できるので、2度目を実行する必要性はかなり低いのですが……。処理が簡単で、GUIアプリをコントロールしていないですし、処理を行ったMacBook Proでは4コアのCPUのほとんどが空いており……並列処理にはもってこいの内容です(が、使い捨ての処理なのでそこまでは……)。

とりあえず、EULAの文章などのサンプルを取り出すために、他の用件を片付けている間にAppleScriptを走らせて処理できました。予想外だったのは、すべてのアプリのアーカイブを展開したら容量が増えてHDDの空き容量が減って危ない目に……。

スクリプト名:iTunesのMobileアプリをコピーしてすべて展開する
–iTunesのデータフォルダを求める
set mFol to path to music folder
set mFolStr to mFol as string
set mFolStr to mFolStr & “iTunes:Mobile Applications:”

–home:Music:iTunes:Mobile Applicationsフォルダ内のipaファイルの一覧を取得する
try
  tell application “Finder”
    tell folder mFolStr
      set appList to (every file whose name ends with “.ipa”) as alias list
    end tell
  end tell
on error
  display dialog “Mobile Applicationsフォルダが存在しないか、Mobileアプリが存在しません。” buttons {“OK”} default button 1 with icon 1
  
return
end try

–ipaファイルを処理するループ
set appProcTmp to choose folder with prompt “iOSアプリケーションを展開する作業フォルダを指定”

set aCount to 1 –フォルダ名のカウンタ

repeat with i in appList
  –ipaからzipにリネーム
  
set j to contents of i
  
  
tell application “Finder”
    
    
–新規フォルダ作成
    
set newFolder to (make new folder at appProcTmp with properties {name:aCount as string})
    
set newFolder to newFolder as alias
    
    
–ipaファイルをコピー
    
set anAppFile to (duplicate j to newFolder)
    
set anAppFile to anAppFile as alias
    
    
–ipaファイルをzipにリネーム
    
set aName to name of anAppFile
    
set bName to retNameFromFilenameStr(aName) of me
    
set bName to bName & “.zip”
    
    
set name of anAppFile to bName –rename
    
  end tell
  
  
–unzipを実行してzipアーカイブを展開
  
set tmpFolPath to POSIX path of newFolder
  
set tmpPathPOSIXfull to POSIX path of anAppFile
  
try
    do shell script “cd “ & quoted form of tmpFolPath & ” && unzip “ & quoted form of tmpPathPOSIXfull
  on error erMes
    return {false, erMes}
  end try
  
–展開ここまで
  
  
  
–展開後のアプリケーションバンドルをただのフォルダに変える
  
tell application “Finder”
    tell folder (newFolder as string)
      tell folder “Payload”
        set appFileList to (every file whose name ends with “.app”)
        
set appFile to (contents of first item of appFileList) as alias
        
        
set tmpName to name of appFile
        
set newAppName to retNameFromFilenameStr(tmpName) of me
        
set name of appFile to newAppName
      end tell
    end tell
  end tell
  
  
  
set aCount to aCount + 1
  
end repeat

–ファイル名文字列から拡張子を外して返す
on retNameFromFilenameStr(fileNameStr)
  set fLen to length of fileNameStr
  
set revText to (reverse of (characters of fileNameStr)) as string –逆順テキストを作成
  
set anOffset to offset of “.” in revText
  
set fRes to text 1 thru (fLen - anOffset) of fileNameStr
  
return fRes
end retNameFromFilenameStr

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

2012/01/05 文字入力モードを制御

GUI Scripting経由でSystemUIServerを制御して、IMの文字入力モードを変更するAppleScriptです。

実行には、GUI Scriptingがオンになっている必要があります。また、現時点ではMac OS X 10.6.8と10.7.2で確認してある状態です(10.5は微妙。10.4ではダメだと思います)。

→ 後日確認したところ、Mac OS X 10.5.8と10.4.11では動きませんでした。予想どおり。

文字入力モードをAppleScriptからコントロールするのは、普通に考えれば無理そうです。

真っ先に思いつくのが、AppleScriptObjCによるGUIつきアプリケーションを作成して、そのアプリケーションのWindow上にNSTextFieldを作成し、入力文字種類を制御するやりかたです。これなら、文字入力を制御できているといえなくもありません。

サードパーティのIMまで目を向けると、ジャストシステムのATOKはATOKダイレクトAPIなるAPIをユーザーに公開しており、コントロールできなくもなさそうな雰囲気はしているのですが、メーカーが想定している使い方しかさせてもらえなさそうな雰囲気も漂っています。

AppleScriptObjC経由で、ことえりのステータスを変更ないしは固定するようなプログラムを呼び出す方法も考えられなくもないですが……すぐには情報が見つかりませんでした。

そこで、きわめてAppleScript的に、GUI Scripting経由でコントロールしてみようということになりました。

画面上部のメニュー右側に表示されるMenu Extraは、SystemUIServerというプログラムが管轄しています(Mac OS X 10.6/10.7)。

menu1.jpg

このため、SystemUIServerのメニューの各アイテムにAppleScriptからアクセスすると、最低限、どのような内容を表示しているか取得できそうです。ユーザーによってMenu Extraの内容や並び順はカスタマイズされまくっているので、Menu Extra同士の識別ができなくてはなりません。

descriptionという属性を調べることで、どのプログラムが表示しているものか識別できそうです。Apple純正のMenu Extraしか値を取得できていない状態ですが、今回の目的のためにはこれで十分です。

並び順からいって、「text input」というのがIMのMenu Extraを表しているようです。

スクリプト名:System UI Serverから何がmenu extraを表示しているかを取得
activate application “SystemUIServer”
tell application “System Events”
  tell process “SystemUIServer”
    tell menu bar 1
      set aList to description of every menu bar item
    end tell
  end tell
end tell
–> {”time machine”, “bluetooth”, “iChat”, “displays”, “AirMac Menu Extra”, “システムサウンド音量”, “AppleScript”, “バッテリーメニュー。 完全充電まで 5 時間 39 分 .”, “clock”, “text input”, “user”}

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

次に、どのプログラムがどのような情報を表示しているのか「value」属性を調べてみました。「英字」「ひらがな」「カタカナ」といった情報が取得できます。けっこういい感じです。

スクリプト名:System UI Serverから各menu extraが何を表示しているかを取得
activate application “SystemUIServer”
tell application “System Events”
  tell process “SystemUIServer”
    tell menu bar 1
      set aList to value of every menu bar item
    end tell
  end tell
end tell
–> {missing value, missing value, missing value, missing value, “Extreme net の 4 本のうち 4 本の信号”, missing value, missing value, missing value, “1月4日(水) 23:49″, “英字”, missing value}

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

ちなみに、このvalueをAppleScriptから変更できないか試してみたのですが、valueはあくまで現状を反映させたものであり、書き換えてみても何も起きませんでした。

そこで、これまたGUI Scripting的なアプローチで、この「text input」のmenu extraをクリックして、表示されたメニューから指定のアイテムをクリックするという操作を行ってみました。

スクリプト名:System UI Serverを操作してIMの文字入力状態を変更する

setInputState(“英字”) of me
–setInputState(”ひらがな”) of me

–IMの入力状態を設定する
on setInputState(aState)
  activate application “SystemUIServer”
  
tell application “System Events”
    tell process “SystemUIServer”
      tell menu bar 1
        set aList to every menu bar item whose description is “text input”
        
set anItem to first item of aList
        
set curVal to value of anItem
        
        
if aState = curVal then return –変更の必要がなければリターン
        
        
tell anItem
          click
          
tell menu 1
            set mList to every menu item whose title is aState
            
set m1Item to first item of mList
            
tell m1Item
              click
            end tell
          end tell
        end tell
        
      end tell
    end tell
  end tell
  
end setInputState

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

人間、やればできるもんです。できないかと思っていたのですが、やってみたら案外手軽にできてしまいました。

ただし、複数のIMがインストールされている環境では、それぞれを識別するのはちょっと難しそうで……

menu2.jpg

もうちょっと調べてみないとなんともいえないところでしょうか。

2011/12/21 miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行 v2

miの最前面のドキュメント上で選択中のテキストをファイルに書き出して、perlのプログラムとしてTerminalで実行するAppleScriptのバグ修正版です。

最初のバージョンでは、改行コードがCRの状態で書き出されていたため、改行コードをLFに置換してから実行するようにしました。

初版では、選択範囲にコメント行が含まれているとエラーになっていたりしましたが、このバージョンではそういうことはありません。

スクリプト名:miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行 v2
tell application “mi”
  tell front document
    set this_data to selection
    
    
–エラー対策
    
if this_data = “” then
      display dialog “文字列が何も選択されていません” buttons {“OK”} default button 1 with icon 1 with title “エラー”
      
return
    end if
    
    
set this_data to this_data as Unicode text
    
  end tell
end tell

–改行コードがCRになっている部分をLFに置換(v2で追加)
set this_data to repChar(this_data, ASCII character 13, ASCII character 10) of me

set tmpPath to path to temporary items from user domain
set fStr to (do shell script “date +%Y%m%d%H%M%S”) & “.pl”
set fPath to (tmpPath as string) & fStr

write_to_fileUTF8(this_data, fPath, false) of me
do shell script “sync”

set sText to “perl -w “ & quoted form of POSIX path of fPath
doComInTerminalWindow(sText) of me

on doComInTerminalWindow(aCMD)
  tell application “Terminal”
    set wCount to count (every window whose visible is true)
    
    
– By wayne melrose
    
– Re: New window in Terminal.app
    
    
if wCount = 0 then
      –ウィンドウが1枚も表示されていない場合
      
do script aCMD
    else
      –すでにウィンドウが表示されている場合
      
do script aCMD in front window
    end if
  end tell
end doComInTerminalWindow

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_fileUTF8(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file as «class utf8» starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_fileUTF8

–文字置換ルーチン
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

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

2011/12/18 miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行

miでオープン中の最前面のドキュメントで選択中のテキストを書き出して、Terminalでperlのプログラムとして実行するAppleScriptです。

コメント行を含む内容を実行するとエラーになるのはなぜなんでしょう?(Perl詳しくないもんで)

mi21.jpg

mi11.jpg

スクリプト名:miで選択中の内容をファイルに書き出してperlのプログラムとしてterminalで実行
tell application “mi”
  tell front document
    set this_data to selection
    
    
–エラー対策
    
if this_data = “” then
      display dialog “文字列が何も選択されていません” buttons {“OK”} default button 1 with icon 1 with title “エラー”
      
return
    end if
    
    
set this_data to this_data as Unicode text
    
  end tell
end tell

–set this_data to “print ‘test’;”

set tmpPath to path to temporary items from user domain
set fStr to (do shell script “date +%Y%m%d%H%M%S”) & “.pl”
set fPath to (tmpPath as string) & fStr

write_to_fileUTF8(this_data, fPath, false) of me
do shell script “sync”

set sText to “perl -w “ & quoted form of POSIX path of fPath
doComInTerminalWindow(sText) of me

on doComInTerminalWindow(aCMD)
  tell application “Terminal”
    set wCount to count (every window whose visible is true)
    
    
– By wayne melrose
    
– Re: New window in Terminal.app
    
    
if wCount = 0 then
      –ウィンドウが1枚も表示されていない場合
      
do script aCMD
    else
      –すでにウィンドウが表示されている場合
      
do script aCMD in front window
    end if
  end tell
end doComInTerminalWindow

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_fileUTF8(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file as «class utf8» starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_fileUTF8

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

2011/12/06 現在表示中のCanvasに存在しているラインのうち青いものに影を付ける

OmniGraffleで、現在表示中のCanvasに存在しているラインのうち青いものだけに影を付けるAppleScriptです。

矢印だけに影を付けるとか、さまざまなフィルター参照が使えると便利なことでしょう。

スクリプト名:現在表示中のCanvasに存在しているラインのうち青いものに影を付ける
tell application “OmniGraffle 5″
  tell canvas of front window
    tell (every line whose stroke color is equal to {0.0, 0.0, 1.0}) –フィルタ参照で青いものだけ抽出
      set draws shadow to true
    end tell
  end tell
end tell

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

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

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