Archive for 6月, 2010

2010/06/27 オープン中のXcodeプロジェクトのフレームワークのパスを取得する

オープン中のXcodeプロジェクトの中に含まれるフレームワークのパスを取得するAppleScriptです。Xcode 3.2.2上でテストしてみました。

xcframew1.jpg

Xcodeプロジェクト内の「Frameworks」グループ内にある「Linked Frameworks」グループと、「Other Frameworks」グループの中に含まれるフレームワークのパス(real path)を取得します。real pathだけでなく、相対パスなどをはじめ、さまざまな情報を取得できるので、まずはpropertiesでプロパティを取得して様子を見てみるとよいのではないでしょうか?

スクリプト名:オープン中のXcodeプロジェクトのフレームワークのパスを取得する
–現在編集中のXcodeプロジェクトのパス情報を取得
tell application "Xcode"
  try
    set aPrj to project of active project document
  on error
    activate
    
display dialog "There is no Xcode Project" with icon 1 buttons {"OK"} default button 1
    
return
  end try
  
  
–各フレームワークの絶対パスを取得
  
tell aPrj
    set gList to name of every group
    
–> {"Scripts", "Resources", "MainMenu.xib", "InfoPlist.strings", "Other Sources", "Frameworks", "Linked Frameworks", "Other Frameworks", "Products"}–名前をとってみただけ
    
    
tell group "Frameworks"
      tell group "Linked Frameworks"
        set cList to contents
        
set linkedFrameworkList to {}
        
repeat with i in cList
          tell i
            set the end of linkedFrameworkList to real path
          end tell
        end repeat
      end tell
      
      
tell group "Other Frameworks"
        set oList to contents
        
set otherFrameworkList to {}
        
repeat with i in oList
          tell i
            set the end of otherFrameworkList to real path
          end tell
        end repeat
      end tell
      
    end tell
  end tell
end tell

log {"linkedFrameworkList", linkedFrameworkList}
(*linkedFrameworkList, /System/Library/Frameworks/Cocoa.framework, /System/Library/Frameworks/AppleScriptKit.framework*)

log {"otherFrameworkList", otherFrameworkList}
(*otherFrameworkList, /System/Library/Frameworks/Foundation.framework, /System/Library/Frameworks/AppKit.framework*)

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

2010/06/24 指定フォルダ内をすべて削除し、そのフォルダも削除

指定フォルダ内のファイルをすべて削除し、指定フォルダ自体も削除するAppleScriptです。

本当は、Finderに命令して指定フォルダをゴミ箱に捨てて(delete)、ゴミ箱をカラにすれば(empty trash)4行で一丁上がりなのですが………………Finder経由で命令すると音が出ていやだとか、ゴミ箱をカラにするのが嫌われるとか(汗)、そういう状況に対応するためのものです。

スクリプト名:指定フォルダ内をすべて削除し、そのフォルダも削除
set a to choose folder
deleteFolderItself(a) of me

–指定フォルダ内をすべて削除し、そのフォルダ自体も削除
on deleteFolderItself(aFol)
  set aU to (POSIX path of aFol)
  
if aU does not end with “/” then
    set aU to aU & “/”
  end if
  
set aU to (quoted form of aU)
  
do shell script “rm -rf “ & aU
end deleteFolderItself

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

2010/06/24 指定のzipアーカイブを展開してファイルを取り出す

指定のZipアーカイブを一時フォルダにコピーして展開し、ファイルを取り出すAppleScriptです。

指定した元ファイルを直接処理せず、一時フォルダにコピーして処理します。zipアーカイブを一時フォルダにコピーして展開し、一時フォルダ内にあるzipファイルを削除。残りのファイルを展開されたものとみなしてリストで返します。

unzipZipArchiveが返してくるリザルトは、{実行結果フラグ, 結果データ}でペアになったリスト型変数です。実行成功だと実行結果フラグがtrueに、失敗するとfalseが返ります。

zip圧縮されているデータがテキストファイルだという前提のもとに、展開したファイルをテキストエディットでオープンしていますが、そこは単なる動作確認部分なので削除してしまってかまいません。

スクリプト名:指定のzipアーカイブを展開してファイルを取り出す。元アーカイブは削除
set a to choose file
set {resF, resData} to unzipZipArchive(a) of me
if resF = false then return –エラーの場合

tell application "Finder"
  set appFile to application file id "com.apple.textedit"
  
open first item of resData using appFile –テキストエディットを使って展開したファイルをオープンする
end tell

–指定のZipアーカイブを展開して結果を返す
on unzipZipArchive(aFile)
  tell application "Finder"
    set aFileName to name of (aFile as alias)
  end tell
  
  
set aPOSIX to POSIX path of aFile
  
  
–コピー先のフォルダ(一時フォルダ)を作成して、指定ファイルをコピー
  
set tmpPath to path to temporary items from system domain
  
set tmpPathPOSIX to POSIX path of tmpPath
  
set rndName to do shell script "uuidgen"
  
set tmpPathPOSIXfull to tmpPathPOSIX & rndName & "/"
  
  
–一時フォルダ内にさらに一時フォルダを掘る
  
try
    do shell script "mkdir -p" & space & quoted form of tmpPathPOSIXfull
  on error erMes
    return {false, erMes}
  end try
  
  
–掘ったフォルダに元のファイルをコピー
  
try
    do shell script "cp " & quoted form of aPOSIX & " " & tmpPathPOSIXfull
  on error erMes
    return {false, erMes}
  end try
  
  
–unzipを実行してzipアーカイブを展開
  
try
    do shell script "cd " & quoted form of tmpPathPOSIXfull & " && " & "unzip " & quoted form of aFileName
  on error erMes
    return {false, erMes}
  end try
  
  
–一時フォルダにコピーしたアーカイブは削除
  
do shell script "rm -f " & quoted form of (tmpPathPOSIXfull & aFileName) –ここはもう、エラートラップはいらないだろー
  
  
set extFol to (POSIX file tmpPathPOSIXfull)
  
  
tell application "Finder"
    tell folder extFol
      set aList to every file as alias list
    end tell
  end tell
  
  
return {true, aList}
  
end unzipZipArchive

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

2010/06/24 指定データをZip圧縮

指定文字列をテキストファイルに書き出して、さらにZip圧縮して返すAppleScriptです。

ありもののファイルを処理するのではなく、文字列データをファイルに書き出してZip圧縮します。

スクリプト名:指定データをzip圧縮
set aRes to do shell script "cd /usr/bin && ls -la"
makeZipFile(aRes) of me
–> "/private/var/folders/UG/UG9cHtbG2Rabs++1YwYvY++++TU/TemporaryItems/B5009213-2C00-46DB-9FF6-17C9BC26FD16.txt.zip"

–指定データをzip圧縮
on makeZipFile(aStr)
  set aPath to (path to temporary items from system domain) as string
  
set afN to (do shell script "uuidgen") & ".txt"
  
  
set aFullPath to aPath & afN
  
write_to_file(aStr, aFullPath, false) of me
  
  
set f1Path to quoted form of POSIX path of aFullPath
  
set f2Path to quoted form of POSIX path of (aFullPath & ".zip")
  
  
try
    with timeout of 3600 seconds
      set aRes to do shell script "/usr/bin/zip -r -j " & f2Path & " " & f1Path
    end timeout
  on error
    return false
  end try
  
  
return (POSIX path of (aFullPath & ".zip"))
end makeZipFile

–ファイルの追記ルーチン「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

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

2010/06/22 指定文字列のAppleScriptをAppleScriptエディタ上で実際に実行して結果を文字列として取得する 10.5_10.6

指定の文字列をAppleScriptとして評価して実行し、結果をすべて文字列で取得するAppleScript……の、Mac OS X 10.5/10.6の両バージョン対応版です。動作には、GUI Scriptingがオンになっている必要があります。

iChat経由でAppleScriptを実行するAppleScriptを作ったまでは良かったのですが、リモート環境にあるMacがMac OS X 10.5で稼働しており、10.5/10.6の両環境で動く必要が出てきました。

AppleScriptを記述したり生成用に使ったりするプログラムは、Mac OS X 10.5までは「スクリプトエディタ」、10.6からは「AppleScriptエディタ」という名前になっており、OSのバージョンを取得してGUI Scriptingでコントロールするパラメータを(ちょっとだけ)変更し、両プログラムのGUIのちがい(割り当てられたキーボードショートカットのキーが異なるとか)を吸収しています。

as106.jpg
▲Mac OS X 10.6上の「AppleScriptエディタ」の「表示」メニュー。「結果を表示」がCommand-3

as105.jpg
▲Mac OS X 10.5上の「スクリプトエディタ」の「表示」メニュー。「結果を表示」がCommand-2

とりあえず、10.4は対象にしなくてよさそうだったので10.4はサポートしていませんが、サポートしたい環境にいる方は、if文で10.4に対応させるといいかもしれません(そもそも、iChatのイベントでAppleScriptを動かす仕組みは10.5から導入されたので、あんまりうまみがありません)。

スクリプト名:指定文字列のAppleScriptをAppleScriptエディタ上で実際に実行して結果を文字列として取得する 10.5_10.6
set aScript to “tell app \”Finder\” to get properties of startup disk”
set asRes to getAppeScriptRes(aScript) of me

–指定文字列のAppleScriptをAppleScriptエディタ上で実際に実行して結果を文字列として取得する
on getAppeScriptRes(aScript)
  tell application id “com.apple.scripteditor2″
    make new document
    
tell window 1
      set bDoc to name
    end tell
    
    
tell document bDoc
      set contents to aScript
      
      
–いきなり実行する
      
try
        execute
      on error
        close without saving
        
return false
      end try
    end tell
    
  end tell
  
  
  
set osVer to system attribute “sys2″
  
  
–GUI Scripting経由でAppleScriptの実行結果を取得する
  
activate application id “com.apple.scripteditor2″
  
tell application “System Events”
    
    
if osVer > 5 then
      –Mac OS X 10.6以降の場合(とりあえず10.6.x)
      
tell process “AppleScript エディタ” –各国語環境で名前は違うかもしれない、と日本語で書いてみる
        keystroke “3″ using {command down} –command-3 結果を表示
        
set resVal to value of text area 1 of scroll area 1 of group 1 of group 1 of splitter group 1 of window 1
      end tell
      
    else if osVer = 5 then
      –Mac OS X 10.5の場合
      
tell process “スクリプトエディタ” –ここも各言語環境で名前が違う、と日本語で書いてみる
        keystroke “2″ using {command down} –command-2 結果を表示
        
set resVal to value of text area 1 of scroll area 1 of group 1 of splitter group 1 of window 1
      end tell
    end if
  end tell
  
  
  
tell application id “com.apple.scripteditor2″
    tell document bDoc
      close without saving
    end tell
  end tell
  
  
return resVal
  
end getAppeScriptRes

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

2010/06/19 ペアになる値のセットを選択

ペアになる値のセットを選択するAppleScriptです。1,2,3の値のリストに対して、赤、青、緑のそれぞれの色を割り当てる場合に、1に対するものはどれか、2に対するものはどれか……と、ユーザーに尋ねて対応リストを作成します。

pair1.jpg

pair2.jpg

pair3.jpg

pair4.jpg

最後に、ペアを構成するリストの最後の項目については、組み合わせパターンが残りのものしか存在しないため、あえてダイアログで確認せずにペアを決定します。

スクリプト名:ペアになる値のセットを選択
set aList to {“10W”, “20W”, “30W”}
set bList to {“あか”, “あお”, “きいろ”}

set resPair to {}
repeat ((length of aList) - 1) times
  set aRes to choose from list aList with prompt “以下の内容から選択”
  
if aRes = false then return
  
set aaRes to contents of first item of aRes
  
  
set bRes to choose from list bList with prompt “「” & aaRes & “」に該当するものを以下から選択”
  
if bRes = false then return
  
set bbRes to contents of first item of bRes
  
  
set the end of resPair to {aaRes, bbRes}
  
  
–サブルーチン「deleteNumfromListByDelList」の仕様で、削除対象データをリストで渡す
  
set aList to deleteNumfromListByDelList(aList, {aaRes}) of me
  
set bList to deleteNumfromListByDelList(bList, {bbRes}) of me
  
end repeat

–最後の項目はユーザーに尋ねなくても、残ったものを使えばよい
set the end of resPair to {contents of first item of aList, contents of first item of bList}

resPair
–> {{”10W”, “あか”}, {”20W”, “あお”}, {”30W”, “きいろ”}}

–指定のリストから削除指定リストに入っているアイテムを削除する
–こちらは、deleteListに入っているデータに該当するものを削除する
on deleteNumfromListByDelList(aList, deleteList)
  set newList to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not in deleteList then
      set the end of newList to j
    end if
  end repeat
  
  
return newList
end deleteNumfromListByDelList

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

2010/06/19 TeamViewerを起動してIDとパスワードを取得する

リモート操作アプリケーション「TeamViewer」を起動してIDとパスワードを取得するAppleScriptです。パラメータとして、相手側マシン上でTeamViewerが起動してID等を取得するのを待つ時間を秒で指定します。

tv1.jpg

リモート操作アプリケーション「TeamViewer」は、iChatでリモート操作できないようなネットワーク環境のマシンでもリモート操作できるなど、かなり「つぶし」の効くソフトです。

たいへん便利なのですが、相手側のマシン上でTeamViewerを起動し、IDとパスワードをメールや電話などで教えてもらう必要があります。この点がけっこう面倒で……それをiChat経由で本Scriptを相手マシンに送り付けてIDとパスワードを調査できれば、それを元にTeamViewerですぐにリモート接続可能になります。

iChatの文字チャット経由で本Scriptを送り付けて実行させれば、相手側マシンではTeamViewerが起動し、そのIDとパスワードをiChat経由で受信することができ、リモートメンテナンスを効率的に行えることでしょう。

スクリプト名:TeamViewerを起動してIDとパスワードを取得する
set aRec to getTeamViewerInfo(30) of me
–> {idInfo:”594 395 137″, passInfo:”9568″}

–TeamViewerを起動して、IDとパスワードを取得する
–ただし、GUI Scriptingをイネーブルにしておくことが必要
on getTeamViewerInfo(waitSeconds)
  repeat waitSeconds times
    activate application “TeamViewer”
    
tell application “System Events”
      tell process “TeamViewer”
        tell window “TeamViewer”
          set idText to value of (static text 1 of group 1)
          
set passText to value of (static text 2 of group 1)
        end tell
      end tell
    end tell
    
    
–両方取得できたらループから抜ける
    
if (idText is not equal to “”) and (passText is not equal to “”) then
      exit repeat
    end if
    
    
delay 1
  end repeat
  
  
return {idInfo:idText, passInfo:passText}
end getTeamViewerInfo

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

2010/06/19 iChat経由で受信したAppleScriptファイルを実行

iChat経由で受信したAppleScriptファイルをAppleScriptエディタ上で実行し、結果をiChatで返すAppleScriptです。

離れた場所にあるマシン用にAppleScriptを作成し、自分の手元では動いていたのに、相手のマシン上で動かなかったりエラーが発生した……というケースに対応すべく、先日シェルコマンドやAppleScreiptコマンドをiChat経由で受信して実行するAppleScriptを作成しましたが……やはり、まとまった処理を実行させたいケースが多く、ちょっとしたコマンドを実行するだけでは満足できません。

そこで、iChat経由でAppleScriptファイルを受信したら実行して結果を返すAppleScriptを作成した次第です。

本AppleScriptは、かならずテキスト形式で保存して、iChatの環境設定>「警告」で「ファイル転送の受信」および「ファイル転送の完了」イベントで本Scriptを実行するよう設定する必要があります。

ic10.jpg

マシンAの上のiChatにこのScriptを設定。マシンB上からiChatでマシンAにチャットを仕掛け……マシンBからiChatウィンドウにAppleScriptファイルをドラッグ&ドロップすると……マシンB側で送信されたAppleScriptを実行し、結果をマシンBに返します。

まだテスト段階のプログラムなので、デスクトップ上に受信したファイル転送のプロパティをログとして書き出します。

ここまで作成できたので、元のプログラムと合体させれば、リモートメンテナンス用のツールとして割と実用性が出てくるのではないでしょうか。

スクリプト名:ichat_file_receive_2.applescript
using terms from application “iChat”
  
  
on received file transfer invitation theFileTransfer
    accept theFileTransfer
  end received file transfer invitation
  
  
on completed file transfer theFileTransfer
    
    
set aProp to properties of theFileTransfer
    
    
–ログに記録
    
set dtPath to path to desktop as string
    
set wFilePath to dtPath & (do shell script “date +%Y%m%d-%H%M%S”)
    
write_to_file_asRec(aProp, wFilePath, false) of me
    
    
set aFile to (file of theFileTransfer) –ファイル
    
set aStatus to transfer status of theFileTransfer –転送状態
    
set aDirection to direction of theFileTransfer –転送方向
    
set sentBuddy to buddy of theFileTransfer –転送してきたBuddy
    
    
if aDirection is not equal to incoming then return –ファイル転送が「受信モード」でなければリターン
    
if aStatus is not equal to finished then return –ファイル転送が終了していなければリターン
    
    
–指定ファイルがAppleScriptかどうかをチェック
    
set aProp to info for (aFile as alias)
    
if kind of aProp = “スクリプト” and file type of aProp = “osas” then
      set {aRes, aReason} to executeAS(aFile) of me
    end if
    
    
if aRes = false then
      send aReason to sentBuddy
    else
      –通常の結果返信
      
send aReason to sentBuddy
    end if
    
  end completed file transfer
  
  
end using terms from

–指定のaliasのAppleScriptをオープンして結果を返す
on executeAS(asFileAlias)
  tell application “AppleScript Editor”
    try
      open asFileAlias
    on error erMes
      return {false, “AppleScriptファイルのオープンに失敗しました。” & erMes}
    end try
    
    
tell window 1
      set bDoc to name
    end tell
    
    
tell document bDoc
      
      
–実行する
      
try
        execute
      on error erMes
        –エラー発生時にはクローズしてfalseを返す
        
close without saving
        
return {false, “AppleScriptファイルの実行時にエラーが発生しました。” & erMes}
      end try
      
    end tell
    
    
–GUI Scripting経由でAppleScriptの実行結果を取得する
    
activate application “AppleScript Editor”
    
tell application “System Events”
      tell process “AppleScript エディタ” –各国語環境で名前は違うかもしれない、と日本語で書いてみる
        keystroke “3″ using {command down} –command-3 結果を表示
        
set resVal to value of text area 1 of scroll area 1 of group 1 of group 1 of splitter group 1 of window 1
      end tell
    end tell
    
    
tell application “AppleScript Editor”
      tell document bDoc
        close without saving
      end tell
    end tell
    
    
return {true, resVal}
    
  end tell
  
end executeAS

–ファイルの追記ルーチン「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

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file_asRec(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 record 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_asRec

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

2010/06/17 指定文字列のAppleScriptをAppleScriptエディタ上で実際に実行して結果を文字列として取得する

指定したAppleScript文字列を、実際にAppleScriptエディタ上で実行して、その結果を「文字列として」取得するAppleScriptです。

一昨日掲載した大物AppleScriptにも、かなり不満が残っていました。それは、「as:」とAppleScriptコマンドを実行させても、結果が文字化けして返ってくることがあることです。各アプリケーション依存のオブジェクト値だったりすると、無理やり文字列に直そうとしても、

“{class:«class cdis», name:\”Cherry\”, index:2, displayed name:\”Cherry\”, name extension:\”\”, extension hidden:false, «class ctnr»:«class pcmp» of application \”Finder\”, «class cdis»:«class sdsk» of application \”Finder\”, «class posn»:{98, 82}, «class dpos»:{1714, 37}, bounds:{66, 50, 130, 114}, kind:\”ボリューム\”, «class labi»:0, locked:false, «class dscr»:missing value, «class comt»:\”\”, size:5.33816377344E+11, «class phys»:5.33816377344E+11, creation date:date \”2010年2月20日土曜日 13:24:06\”, modification date:date \”2010年6月17日木曜日 11:00:56\”, «class iimg»:missing value, URL:\”file://localhost/\”, «class sown»:\”システム\”, «class sgrp»:\”admin\”, «class ownr»:«constant ****rdwr», «class gppr»:«constant ****rdwr», «class gstp»:«constant ****read», «class cwnd»:«class cwnd» of «class sdsk» of application \”Finder\”, id:-100, «class capa»:6.39791054848E+11, «class frsp»:1.05974677504E+11, «class isej»:false, «class istd»:true, «class dfmt»:«constant ****dfh+», «class Jrnl»:true, «class isrv»:true, «class igpr»:false} “

のように、残念な結果が得られてしまいます。

そこで、パーフェクトに指定のAppleScriptの実行結果を取得できる方法を検討してみました。

こういう用途になると、がぜん登場してくるのが、AS系のブラックテクノロジーともいえる「GUI Scripting」。将来の互換性とか、アプリケーションがバージョンアップしたら使えなくなるかもしれないとか、スピードがいまいちとか、そういうことにすべて目をつぶって「目的だけ果たせればそれでいい」という手段です。

前出の実行結果も、

“{class:disk, name:\”Cherry\”, index:2, displayed name:\”Cherry\”, name extension:\”\”, extension hidden:false, container:computer container of application \”Finder\”, disk:startup disk of application \”Finder\”, position:{98, 82}, desktop position:{1714, 37}, bounds:{66, 50, 130, 114}, kind:\”ボリューム\”, label index:0, locked:false, description:missing value, comment:\”\”, size:5.3381421056E+11, physical size:5.3381421056E+11, creation date:date \”2010年2月20日土曜日 13:24:06\”, modification date:date \”2010年6月17日木曜日 11:00:56\”, icon:missing value, URL:\”file://localhost/\”, owner:\”システム\”, group:\”admin\”, owner privileges:read write, group privileges:read write, everyones privileges:read only, container window:container window of startup disk of application \”Finder\”, id:-100, capacity:6.39791054848E+11, free space:1.05976844288E+11, ejectable:false, startup:true, format:Mac OS Extended format, journaling enabled:true, local volume:true, ignore privileges:false}”

……と、きちんと得られるようになり、「もうちょっと手を入れてみようか」と欲が出てきてしまうところです。

スクリプト名:指定文字列のAppleScriptをAppleScriptエディタ上で実際に実行して結果を文字列として取得する
set aScript to “tell app \”Finder\” to get properties of startup disk”
set asRes to getAppeScriptRes(aScript) of me

–指定文字列のAppleScriptをAppleScriptエディタ上で実際に実行して結果を文字列として取得する
on getAppeScriptRes(aScript)
  tell application “AppleScript Editor”
    make new document
    
tell window 1
      set bDoc to name
    end tell
    
    
tell document bDoc
      set contents to aScript
      
      
–いきなり実行する
      
try
        execute
      on error
        close without saving
        
return false
      end try
    end tell
    
  end tell
  
  
–GUI Scripting経由でAppleScriptの実行結果を取得する
  
activate application “AppleScript Editor”
  
tell application “System Events”
    tell process “AppleScript エディタ” –各国語環境で名前は違うかもしれない、と日本語で書いてみる
      keystroke “3″ using {command down} –command-3 結果を表示
      
set resVal to value of text area 1 of scroll area 1 of group 1 of group 1 of splitter group 1 of window 1
    end tell
  end tell
  
  
tell application “AppleScript Editor”
    tell document bDoc
      close without saving
    end tell
  end tell
  
  
return resVal
  
end getAppeScriptRes

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

2010/06/15 iChatの文字チャット経由で遠隔地のMacを操作

iChatの文字チャット経由で遠隔地のMacを操作するAppleScriptです。

ic4.jpg

メッセージの先頭に「sh:」と書けば、その後の文字をシェルコマンドとして、「as:」と書けばその後の文字列をAppleScriptとして実行します。イタズラしようと思えば際限なくイタズラできてしまうので、たいへん危険なAppleScriptであり、取扱いに最大限の注意を必要とします。動作原理を理解できなかったり、シェルコマンドの実行に習熟していないユーザーには使用をすすめません。

Mac OS X 10.5以降、iChatごしに画面共有して遠隔地のMacをコントロールできるようになり、大変便利に活用しています。ただ、そうしたビデオチャットの環境がネットワーク的にできなかったりするケースもあり、それでもリモートコントロールしなければならない、といった事態も考えられます。

そこで、だいたいどこでもつながる文字チャットを使って、shellやAppleScriptのコマンドを送り、文字チャット経由で結果を確認することができるプログラムを(ちょろっと)書いてみました。とりあえず、2台のMacを並べてiChat経由でリモート操作できています。

まずは、動作原理の説明から。

ic1.jpg

iChatは、幾多のバージョンアップを経て、さまざまなアプリケーション内部のイベント(ビデオチャットを仕掛けられたとか、テキストチャットを受信したとか)に応じて、音を鳴らしたり、Dockのアイコンをジャンプさせたり、Text To Speechを利用して文字を読み上げることができるようになっています。この設定は、iChatの「環境設定」の、「警告」で行うことができます。

ic0.jpg

それらの一環としてAppleScriptを呼び出すことができるようになっています。OS標準では、各種チャットを自動受付するAppleScriptなどが用意されており、これらは/Library/Scripts/iChat/に入っています。プログラムの書き方は、それらのサンプルを読めばだいたい分るようになっています。

問題は、iChat内で実行を指定するAppleScriptは、普通のスクリプトとして保存すると実行できないという点です。明示的にAppleScriptエディタ上で「テキスト」として保存して使用しなければなりません。

本Scriptでは、テキストチャットで話しかけられたときのイベント「メッセージを受信」と、テキストチャット中にメッセージを受信したときのイベント「チャットルームで受信したメッセージ」で、このAppleScript(テキスト形式で保存)を指定する必要があります。

本AppleScriptを仕掛けたマシンに、インターネット経由でiChatの文字チャットを開始し、冒頭で紹介したとおり、「sh:」とか「as:」といった文字で始まるメッセージを送信すると、それぞれシェルコマンドやAppleScriptとして評価・実行が行われます。asコマンドを送る際に、「application」を「app」などと略すことも可能です。

もう少し改良して、チャット経由でAppleScriptファイルを送りつけると、実行結果を文字チャットで返してくるとかいったところまでやってみたいです。また、シェルの実行に関してもdo shell scriptコマンドではなくTerminal.appで実行するようにすれば、ワンショットの一発コマンドやり逃げではなく、もう少し実用性が出てくるのではないかと考えます。

ic3.jpg
▲shellコマンドでコントロール

ic2.jpg
▲AppleScriptでコントロール

プログラム的には、ぜんぜん大したことをやっていないのですが、iChatで実行するAppleScriptの注意点がひとつ。プログラムを修正したら再度iChatの環境設定でScriptを指定し直す必要があります。元のプログラムを直しても、指定し直さないとiChat側に変更が通知されないようになっています。

つまり、これをやらないと、修正前のScriptが実行され続けてしまうことになります。そのため、不具合修正がちょっとやりづらい、といったところでしょうか。iChatからのイベントを受信する部分は最低限の記述にしておき、処理本体は外部のアプレット(AppleScriptアプリケーション。常時起動タイプ)で行うとよいかもしれません。

実際に使ってみて……シェルコマンドの実行結果が複数行にわたってしまうと(psコマンドの実行結果とか)、1行目以外は結果が返ってきませんね。やはり、複数行になってしまう場合には結果をテキストファイルに書き出して、iChatのファイル転送機能を使って送ってくるようにしたいところです。ただ……こんな特殊用途のどーーでもいい使い捨てプログラムに、そんなに入れ込んでもしょうがないような、、、、

スクリプト名:iChat_mes_test.applescript
using terms from application “iChat”
  
  
– 最初にテキストチャットで話しかけられた場合の対応(「メッセージを受信」で指定)
  
on received text invitation theMessage from theBuddy for theChat
    accept theChat
    
send “Welcome to shell/AppleScript Remote Control. “
  end received text invitation
  
  
– チャットが成立した後のメッセージ受信(「チャットルームで受信したメッセージ」で指定)
  
on message received theMessage from theBuddy for theChat
    
    
set aRes to “”
    
    
–set theResponse to “ぴよ〜” & theMessage
    
if (theMessage begins with “as:”) or (theMessage begins with “sh:”) then
      set aRes to execCommand(theMessage) of me
    end if
    
    
if aRes is not equal to “” then
      send aRes to theChat
    end if
    
  end message received
  
  
on execCommand(aText)
    if aText begins with “as:” then
      set bText to text 4 thru -1 of aText
      
try
        set aRes to run script bText
        
set aClass to class of aRes
        
if aClass = list then
          set aRes to listToText(aRes) of me
        else if aClass = record then
          set aRes to recordToText(aRes) of me
        else
          set aRes to aRes as string
        end if
        
return aRes
      on error aMes
        return aMes
      end try
      
    else if aText begins with “sh:” then
      set bText to text 4 thru -1 of aText
      
try
        return (do shell script bText)
      on error aMes
        return aMes
      end try
    end if
  end execCommand
  
  
  
  
  
on listToText(aList)
    set listText to {“{”}
    
set quotChar to ASCII character 34
    
set firstFlag to true
    
repeat with i in aList
      set j to contents of i
      
set aClass to class of i
      
if (aClass = integer) or (aClass = number) or (aClass = real) then
        set the end of listText to (getFirst(firstFlag) of me & j as text)
        
set firstFlag to false
      else if (aClass = string) or (aClass = text) or (aClass = Unicode text) then
        set the end of listText to ((getFirst(firstFlag) of me & quotChar & j as text) & quotChar)
        
set firstFlag to false
      else if aClass is list then
        set the end of listText to (getFirst(firstFlag) of me & listToText(j)) –ちょっと再帰処理
        
set firstFlag to false
      else if aClass is record then
        set the end of listText to (getFirst(firstFlag) of me & recordToText(j))
        
set firstFlag to false
        
      end if
    end repeat
    
set the end of listText to “}”
    
set listText to listText as text
    
return listText
  end listToText
  
  
on getFirst(aFlag)
    if aFlag = true then return “”
    
if aFlag = false then return “,”
  end getFirst
  
  
on recordToText(aRec)
    try
      set a to aRec as string
    on error aMsg
      set a to aMsg
    end try
    
    
set b to repChar(a, “のタイプを string に変換できません。”, “”)
    
return b
  end recordToText
  
  
–文字置換ルーチン
  
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
  
  
end using terms from

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

2010/06/13 MacJournalで指定キーワードを含むジャーナルをリストアップ

MacJournalで、指定のキーワードを含むジャーナルを検索して、結果をリストアップし、選択したジャーナルを選択状態にするAppleScriptです。

MacJournalでは、キーワードを入力するとジャーナルエントリー(記事)を全文検索する機能が付いています。Mac OS XのSpotlightのようなもので、たしかに高速で便利なのですが……検索対象はあくまでジャーナルエントリー(記事)であって、ジャーナル(フォルダ)ではありません。

この、ジャーナル(フォルダ)自体だけを検索するための機能が存在しないため、自前で作ってみました。ヘタにジャーナルを大量に作ってしまったため、複数のジャンルに重複したジャーナルを作っていないかどうか、作ろうと思ったジャーナルがすでに存在しているのではないか……といったことを調べるのに、こうしたAppleScriptがないと大変です。というより、標準でこうした機能は用意してほしいところです。

ジャーナルの検索は、すべてのジャーナルを再帰でピックアップして、キーワードに合致するものだけを抽出リストに追加していきます。処理ロジックはかなり単純です。作る前には処理速度に不安があったのですが、ジャーナル(フォルダ)はジャーナルエントリー(記事)ほどには多くないので、それほど処理対象も無闇に増えず、常識的な処理時間で(MacBook Pro Core i7 2.66GHzで、1秒以下。検索対象はウンザリするほどジャーナルを作りまくった自分のMacJournal書類)。

mj1.jpg
▲検索キーワードを入力

mj2.jpg
▲キーワードがヒットした(含む)ジャーナル一覧を表示。心配したとおり、「デジタルラジオ」のジャーナルが複数存在していた

mj3.jpg
▲一覧から選択したジャーナルを選択状態に

journal entryをselected journal entryにする機能はあるのですが、joiurnalそのものを選択状態にする機能がないため、選択したjoiurnalのjournal entry 1を選択状態にしています。その際にエラーが生じたら(指定journalにjournal entryが1件もなかったら)、エラートラップで対応しています。

スクリプト名:MacJournalで指定キーワードを含むジャーナルをリストアップ
global nList, rList, nnList, rrList
set nList to {}
set rList to {}

set nnList to {}
set rrList to {}

set aKey to text returned of (display dialog “Journalの検索対象キーワードを入力してください” default answer “”)

tell application “MacJournal”
  tell document 1
    set j1List to every journal
    
set nList to every journal whose name contains aKey
    
if nList is not equal to {} then
      set nnList to nnList & nList
      
set rrList to rrList & name of every journal whose name contains aKey
    end if
    
getJournal(j1List, aKey) of me
  end tell
end tell

–ヒットしたジャーナル名称一覧を表示して、ユーザーに選択してもらう
set aRes to choose from list rrList default items (contents of first item of rrList)
if aRes = false then return –キャンセルされた場合の対応
set aRes to contents of first item of aRes

–選択した項目をジャーナル名リストから検索し、ジャーナル項目リストから合致したインデックスの項目を取り出す
set aCount to 1
set aF to false
repeat with i in rrList
  set j to contents of i
  
if j = aRes then
    set aF to true
    
exit repeat
  end if
  
set aCount to aCount + 1
end repeat
if aF = false then
  display dialog “MISS(あり得ないエラー?)”
  
return
end if

–実際に、MacJournal上で選択・表示する
tell application “MacJournal”
  set aResJounal to contents of (item aCount of nnList)
  
  
activate
  
tell document 1
    try
      set selected entry to journal entry 1 of aResJounal –ここがミソ!!
    on error
      display dialog “指定のジャーナルにはエントリーが1つも存在しませんでした(T_T)” buttons {“OK”} default button 1 with icon 1
    end try
  end tell
end tell

on getJournal(aList, aKey)
  repeat with i in aList
    tell application “MacJournal”
      tell i
        set j1List to every journal
        
set nList to (every journal whose name contains aKey)
        
if nList is not equal to {} then
          set nnList to nnList & nList
          
set rrList to rrList & (name of every journal whose name contains aKey)
        end if
        
getJournal(j1List, aKey) of me
      end tell
    end tell
  end repeat
end getJournal

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

2010/06/12 編集中のXcodeプロジェクトに含まれる各Scriptの行数をレポート

Xcodeで編集中のプロジェクトに含まれる各AppleScriptの行数をレポートするAppleScriptです。

まずは、Xcode 3.1.4(Mac OS X 10.5)もしくは3.2.2(Mac OS X 10.6)上でAppleScript Studio/AppleScriptObjCなどのプロジェクトをオープンしている状態で、本Scriptを実行します。

すると、プロジェクトに含まれる(正確にいえば、xcodeprojファイルと同階層にある)「.applescript」のファイルをすべて集計し、プロジェクト全体のAppleScriptの行数と、各Scriptの行数をダイアログで表示します。

xcode1.jpg

なが〜いプログラムを書いてしまったときに、どのへんのプログラムをそろそろ再度分割しなければならないだろうか、といった目安にしたり、こんなにプログラムを書いてしまったのかとイヤ〜な気分になりたい時に使います。

別に、AppleScriptでなくてもObjective-CのプログラムでもC++のプログラムでも集計できますし(拡張子でファイルを指定している箇所を変更するだけ)、プロジェクトの下位フォルダの内容をすべて(entire contentsで)たどって調べてもよいでしょう。

スクリプト名:編集中のXcodeプロジェクトに含まれる各Scriptの行数をレポート
–現在編集中のXcodeプロジェクトのパス情報を取得
tell application “Xcode”
  try
    set aPrj to project of active project document
  on error
    activate
    
display dialog “There is no Xcode Project” with icon 1 buttons {“OK”} default button 1
    
return
  end try
  
  
tell aPrj
    set projName to name
    
set projPath to real path
  end tell
end tell

–現在編集中のプロジェクトに含まれるInfo.plistファイルを検出
set projPath to POSIX file projPath

tell application “Finder”
  set parantFol to (folder of file projPath) as alias
  
tell folder parantFol
    set asList to (every file whose name ends with “.applescript”) as alias list
  end tell
end tell

set resList to {}
set tNum to 0
repeat with i in asList
  tell application “Finder”
    set aName to name of i
  end tell
  
  
set aPosixPath to quoted form of POSIX path of i
  
set aRes to do shell script (“wc -l “ & aPosixPath)
  
set aNum to first item of words of aRes
  
  
  
set tNum to tNum + (aNum as number)
  
set the end of resList to ((aNum as string) & “—–” & aName)
  
end repeat

choose from list resList with prompt (“プロジェクト「” & projName & “」(総行数=” & tNum as string) & “行)内の各Scriptの行数” with title “Script行数レポート”

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

2010/06/10 指定のアプリケーションの起動を待つ

IDで指定した複数のアプリケーションの起動を行い、起動が行われたかどうかチェックを行うAppleScriptです。

とりあえずループで300秒待っていますが、もっと短い時間で済むはずです。

スクリプト名:指定のアプリケーションの起動を待つ
set appIDlist to {“com.apple.Automator”, “com.apple.mail”, “com.adobe.Photoshop”, “com.adobe.distiller”}

set aRes to checkAppInstallation(appIDlist) of me
if aRes = false then return –指定IDのアプリケーションが実行環境にインストールされていない場合はリターン

–起動実行
repeat with i in appIDlist
  tell application id i to launch
end repeat

–起動チェック
repeat 300 times
  if waitAppsActive(appIDlist) of me = true then
    exit repeat
  end if
  
delay 1
end repeat

–指定IDのプロセスが起動しているか確認
on waitAppsActive(appIDlist)
  tell application “System Events”
    set appList to bundle identifier of every process whose visible is true
  end tell
  
  
repeat with i in appIDlist
    if i is not in appList then return false
  end repeat
  
  
return true
end waitAppsActive

–指定IDのアプリケーションがHDD上に存在するかを確認
on checkAppInstallation(appIDlist)
  repeat with i in appIDlist
    tell application “Finder”
      try
        set aRes to exists of application file id i
      on error
        return false
      end try
    end tell
  end repeat
  
  
return true
end checkAppInstallation

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

2010/06/10 Numbersで指定シート上の最初のテーブルと同じデータとサイズでシート&テーブルを連続作成

Numbersで、オープン中の書類の中にあるシートのうち、指定のものの中にあるテーブルをコピーして、同じものを複数追加するAppleScriptです。

Numbers 2のAppleScript対応機能は不十分で、本当にやりたいことはほとんどできません。現在表示中のワークシートを取得することも、選択中のテーブルを取得することも、指定のオブジェクト(ワークシート/テーブル)を選択状態にすることもできません。機能のないないづくしです。

現在表示中のワークシート内のテーブルの内容をコピーすることはできないので、とりあえずワークシートの名前一覧を取得し、その中から選択して、テーブルをコピーすることに。

num1.jpg
▲Numbersで書類をオープンしているところ

num2.jpg
▲書類内のワークシート名称一覧から選択

num3.jpg
▲指定のワークシート内のテーブル1から、幅と高さ、内容データを取得して同じものを追加

スクリプト名:Numbersで指定シート上の最初のテーブルと同じデータとサイズでシート&テーブルを連続作成
tell application "Numbers"
  tell document 1
    set sList to name of every sheet
    
set aRes to choose from list sList
    
if aRes = false then return –キャンセルした場合はリターン
    
set aSheetName to first item of aRes
    
    
–指定シート上のテーブル1をスキャンして学習する
    
tell sheet aSheetName
      tell table 1
        set widthNum to column count –幅
        
set hightNum to row count + 1 –高さ
        
set aRange to name of cell range –セル範囲
        
set dataList to value of every cell –データ
      end tell
    end tell
    
    
–同じものをひたすら連続して作成する
    
repeat with i from 1 to 4 –回数は適当(根拠なし)
      set sRes to make new sheet
      
tell sRes
        tell table 1
          set column count to widthNum
          
set row count to hightNum
          
          
set selection range to range aRange
          
          
set cellList to cell of selection range
          
set iCount to 1
          
          
repeat with ii in cellList
            tell ii
              set aVal to (item iCount of dataList)
              
              
–このへんはお好きなように(空きセルに0が入るのがいやだったもので)
              
if aVal is not equal to 0 then
                set value to (aVal as string)
              end if
              
            end tell
            
set iCount to iCount + 1
          end repeat
          
        end tell
      end tell
    end repeat
  end tell
  
end tell

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

2010/06/10 Xcodeで表示中のASのファイルからコメントだけ抽出

Xcodeで表示中のAppleScriptのプログラム(おそらく、AppleScript Studio)から、「–」ではじまるコメント行だけを抽出して、表示するAppleScriptです。

Xcode上で編集中のAppleScript Studioのプログラムを解析してコメントだけを抜き出すAppleScript、ということになります。

ass1.jpg
▲赤い行がコメント(本Blogでもおなじみの書式)

ass2.jpg
▲本AppleScriptを実行したところ。プログラム中のコメントだけを抜き出してダイアログ表示

なが〜いプログラムを組んでしまうと、ファイル数は増えるわ、全体の構造は忘れがちだわで……たまに、全体の構造を見直してみたくなる時があります。そういう場合に使えることでしょう。

行の途中からはじまるコメントには対応していませんし、「(*」〜「*)」でかこったコメントも対応していませんが、そこは個人の趣味なので。そこまで拾って表示したい方は、ぜひ手を加えてみてください。

Xcode 3.1.4 on Mac OS X 10.5.8と、Xcode 3.2.2 on Mac OS X 10.6.3上で動作確認してあります。

ちなみに、Xcode 3.1.4やXcode 3.2.2上ではAppleScript StudioのソースコードをUTF-8で書けるようになっているので、UTF-8で書くようにしています。そのため、AppleScript Studioのソースコードファイルを読むときにUTF8を指定して読み込んでいます。

スクリプト名:Xcodeで表示中のASのファイルからコメントだけ抽出
tell application “Xcode”
  set wCount to count every window
  
if wCount = 0 then
    display dialog “Xcodeで何らかのプロジェクトをオープンした状態で実行してください” buttons {“OK”} default button 1 with icon 1
    
return
  end if
  
  
try
    save
  on error
    return
  end try
  
  
tell window 1
    set aPath to associated file name
  end tell
  
end tell

–Xcode上で表示中の内容がAppleScriptでなければリターン
if aPath does not end with “.applescript” then
  return
end if

–ファイルシステム上から表示中のファイルを直接読み込み
set aText to read POSIX file aPath as «class utf8»
set aList to paragraphs of aText

–ハンドラリストを作成
set hList to {}
set hCount to 1
repeat with i in aList
  set j to contents of i
  
  
ignoring white space
    if j begins with “–” then
      set a to retUTF8(j) of me
      
set the end of hList to a
    end if
  end ignoring
  
  
set hCount to hCount + 1
end repeat

choose from list hList with prompt “Xcodeで表示中のAppleScriptのコメント” with title “コメント抽出結果:”

on retUTF8(theText)
  set the clipboard to (theText as international text) –International はunicodeの同義語
  
set ss to (the clipboard as record)
  
return «class utf8» of ss
end retUTF8

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

2010/06/09 指定のリストから削除指定リストに入っているアイテムを削除する

指定のリストから、「削除指定リスト」に入っているアイテムを削除するAppleScriptです。

削除指定リストには、何アイテム目のリスト項目を削除するかを指定しておきます。ほとんど組み捨てレベルのAppleScriptで、何度書いたか覚えていませんが……とりあえず。

スクリプト名:指定のリストから削除指定リストに入っているアイテムを削除する
set deleteList to {1, 3, 5} –削除アイテムリスト
set aList to {{1, 1, 1}, {2, 2, 2}, {3, 3, 3}, {4, 4, 4}, {5, 5, 5}} –元リスト

set aRes to deleteItemfromListByDelList(aList, deleteList) of me
–> {{2, 2, 2}, {4, 4, 4}}–元リストから削除アイテムリストをもとに削除を行った結果

–指定のリストから削除指定リストに入っているアイテムを削除する
on deleteItemfromListByDelList(aList, deleteList)
  
  
set itemCounter to 1
  
set newList to {}
  
  
repeat with i in aList
    set j to contents of i
    
if itemCounter is not in deleteList then
      set the end of newList to j
    end if
    
set itemCounter to itemCounter + 1
  end repeat
  
  
return newList
end deleteItemfromListByDelList

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

2010/06/09 deleteListに入っている数値に該当するものをリストから削除する

削除リストに入っている数値に該当するものをリストから削除するAppleScriptです。

削除リストには、削除対象データが入ります。

スクリプト名:deleteListに入っている数値に該当するものをリストから削除する
set deleteList to {1, 3, 5} –削除データリスト
set bbList to {1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 10, 6, 10, 10, 10}

set bbbList to deleteNumfromListByDelList(bbList, deleteList) of me
–> {2, 10, 6, 10, 10, 10}

–指定のリストから削除指定リストに入っているアイテムを削除する
–こちらは、deleteListに入っている数値に該当するものを削除する
on deleteNumfromListByDelList(aList, deleteList)
  set newList to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not in deleteList then
      set the end of newList to j
    end if
  end repeat
  
  
return newList
end deleteNumfromListByDelList

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

2010/06/09 リストから、指定データが何アイテム目に登場するかを算出

リストから、指定データが何アイテム目に登場するかを検索して返すAppleScriptです。

スクリプト名:リストから、指定アイテムが何アイテム目に登場するかを算出
set aList to {1, 2, 3, 4, 5, 10, 11, 12}

set a to 10

set b to countItemNumFromList(aList, a) of a
–> 6

–リストから、指定アイテムが何アイテム目に登場するかを算出
on countItemNumFromList(aList, a)
  set aCount to 1
  
repeat with i in aList
    set j to contents of i
    
if j = a then
      return aCount
    end if
    
set aCount to aCount + 1
  end repeat
end countItemNumFromList

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

2010/06/06 iPhotoで横幅が400ピクセルより小さい写真をすべて削除する

iPhotoで「横幅(width)が400ピクセルより小さい写真」をすべて削除するAppleScriptです。

よく、初心者が陥りがちなハマりポイントなのですが、削除系や移動系のコマンドでリストに入れたオブジェクトを一括で指定できるケースが多いのに、1つ1つループで処理している場合があります。

Finderのmoveコマンドやdeleteコマンド、Mail.appのメッセージの移動など、リストに入れた複数のオブジェクトに対して一括命令できるようになっていることが多いので、試してみることをおすすめします。

スクリプト名:iPhotoで横幅が400ピクセルより小さい写真をすべて削除する
with timeout of 36000 seconds
  tell application "iPhoto"
    remove (every photo whose width < 400)
  end tell
end timeout

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

2010/06/06 ファイルのコピー(通常実行、非同期高速実行)

ファイルのコピーを行うAppleScriptです。

通常の記述例と、非同期実行(ignoring application responces)による高速実行の両方を書いておきます。

ファイルコピーの動作は、ファイルのサイズ(巨大なファイルのコピーには時間がかかります)や、ネットワーク回線の太さ(遅い回線の上にあるサーバーのファイルをコピーするには時間がかかります)に依存します。もちろん、実行するマシンの処理速度にも影響を受けます。

ファイルコピー動作がその後の処理に影響を及ぼさない(コピーしたファイルを対象とした動作を行わない)のであれば、非同期実行の指定を行ってしまうのもひとつの手です。ただし、非同期実行を行った場合には、コピー中にエラーが発生(容量オーバーなど)した場合でもそのエラーに対処することはできません。

スクリプト名:ファイルのコピー
set a to choose file with prompt "コピーするファイルを選択"
set b to choose folder with prompt "コピー先のフォルダを選択"

–通常コピー
tell application "Finder"
  duplicate a to b with replacing –with replacingは、同名のファイルがあった場合に上書きする指定
end tell

–処理終了を待たずに次の処理へ(非同期実行)
ignoring application responses
  tell application "Finder"
    duplicate a to b with replacing
  end tell
end ignoring

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

2010/06/06 ファイル名を変更する

ファイル名を変更するAppleScriptです。もう、このあたりになるといちいち保存するレベルではなく、「組み捨て」ですが、たまに質問が来たりするので掲載を。

Finderでさまざまなファイル操作を行う方法については、やや「Finder独自な」やりかたが多いので、まとめておくとよいのかもしれません。

スクリプト名:ファイル名を変更する
set a to choose file –aliasが取得される
set b to (text returned of (display dialog "新しいファイル名を入力:" default answer "")) –とりあえずダイアログから入力しているが、普通こんなのんびりしたことはやらない

tell application "Finder"
  set name of a to b –ファイル名を変更する
end tell

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

2010/06/02 Xcodeで表示中のScriptの各ハンドラ行数をカウント

handlercount.jpgXcodeで表示中のAppleScript Studioのプログラムの、各ハンドラ行数をカウントして、長い順にソートして、画面表示するAppleScriptです。Xcode 3.2.2+Mac OS X 10.6.3で動作確認を行っていますが、Xcode 3.1.4+10.5.8でも動作するはずです。

つまりこれは、「Xcode上のAppleScriptのプログラム」を解析するAppleScriptのプログラムです。

AppleScript Studioのプログラムは、1ファイルあたりの編集・実行可能な上限が存在するため、だいたい1000行をメドに複数のファイルに分割する必要があります。Xcode 3.1.4+Mac OS X 10.5.8の環境では、2500行ぐらいに達するとビルドできなくなったり、意味不明のエラーが発生するようになってしまいます。

そんなわけで、行数が増えてくるとハンドラ単位で別ファイルに分割して、分割ローディングする必要が出てくるわけですが、その分割候補となるのが行数の長いハンドラ。

各ハンドラ/イベントハンドラの行数を集計し、長い順にソートし、ダイアログ表示します。AppleScript Studioのプログラムが対象であり、AppleScript ObjCのプログラムは対象にしていません(短いのはテストしているんですが、ASOCではそんなに長いのを組んでいないもので)。

XcodeのエディタでAppleScriptエディタなみにScriptの書式情報にアクセスできれば、かなり突っ込んだ処理も可能なのですが……指定のハンドラと、それに関連するサブルーチンをまとめて別のScriptファイルに移動させるといったことも処理できそうではあるのですが、現状のXcodeの用語辞書では非常に困難です。

スクリプト名:Xcodeで表示中のScriptの各ハンドラ行数をカウント
tell application “Xcode”
  set wCount to count every window
  
if wCount = 0 then
    display dialog “Xcodeで何らかのプロジェクトをオープンした状態で実行してください” buttons {“OK”} default button 1 with icon 1
    
return
  end if
  
  
try
    save
  on error
    return
  end try
  
  
tell window 1
    set aPath to associated file name
  end tell
end tell

–Xcode上で表示中の内容がAppleScriptでなければリターン
if aPath does not end with “.applescript” then
  return
end if

–ファイルシステム上から表示中のファイルを直接読み込み
set aText to read POSIX file aPath
set aList to paragraphs of aText

–ハンドラリストを作成
set hList to {}
set hCount to 1
repeat with i in aList
  set j to contents of i
  
  
ignoring white space
    if ((j begins with “on”) and (j does not contain “on error”)) or ((j begins with “to”) and (j does not contain “on error”)) then
      
      
if j contains “(” and j contains “)” then
        –ハンドラ
        
set j1 to offset of “on “ in j
        
set j2 to offset of “(” in j
        
        
set aHandle to text (j1 + 3) thru (j2 - 1) of j
        
      else if j contains “theObject” then
        –イベントハンドラ
        
set j1 to offset of “on “ in j
        
set j2 to offset of ” theObject” in j
        
        
set aHandle to text (j1 + 3) thru (j2 - 1) of j
      end if
      
      
set the end of hList to {aHandle, hCount}
    end if
  end ignoring
  
  
set hCount to hCount + 1
end repeat

–各ハンドラ末尾をサーチして{ハンドラ名、開始行、終了行、行数}をリスト化
set hCount to 1
repeat with i from 1 to (length of hList)
  set {aHandler, sLine} to contents of item i of hList
  
repeat with h from (sLine + 1) to (length of aList)
    set aCon to contents of item h of aList
    
    
ignoring white space
      if aCon begins with “end “ & aHandler then
        set the end of item i of hList to h
        
set the end of item i of hList to (h - sLine)
        
exit repeat
      end if
    end ignoring
    
  end repeat
end repeat

–降順ソート
set h2List to shellSortListDecending(hList, 4) of me

–表示用のリストを作成
set h3List to {}
repeat with i in h2List
  set {hName, sNum, eNum, tNum} to i
  
set aCon to (tNum as string) & “—–” & hName
  
set the end of h3List to aCon
end repeat

choose from list h3List with prompt “Xcodeで表示中のAppleScriptの各ハンドラ/イベントハンドラの行数” with title “ハンドラ集計結果:”

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  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 keyItem of item (j - h + 1) of a) < (item keyItem of 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 shellSortListDecending

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