Archive for the 'shell script' Category

05/13 モニタの解像度を取得する(複数モニタ対応)

Mac本体に接続されたディスプレイ(モニタ)の解像度を取得するAppleScriptについて、これまでにいろいろなものを紹介してきました。

なぜ、1つのやり方ですまないかといえば……System Eventsなどで直接ディスプレイ解像度を取得するような命令セットが用意されてこなかったためで……さまざまなやり方を組み合わせて求めてきました(とくに、AppleScript Studioのプログラム内に入れたときに使えるかどうかが問題でした。けっこう、Finderに対する命令がきかない場合があったりで、冷や汗をかかされました)。

scrn1.png

このような環境で試してみましたが、iPad 3をAir Displayを使って外付けディスプレイとして接続した場合には、「環境設定」には表示されるものの、OSの各種コマンドでAir Display接続したiPadの情報は取得できません。

disp5.png

scrn3.png

scrn2.png

注:以下のサンプルAppleScriptは、Air Display経由のiPad 3を「外した」状態で(MacBook Pro本体+外付けLCDの2モニタ構成で)実行しています

■方法1 FinderのDesktop WindowのScroll Areaのsizeを取得

複数ディスプレイのトータルのサイズを取得。個別のディスプレイのサイズについては取得できません。

スクリプト名:displaySize1
–http://piyocast.com/as/archives/73
tell application “System Events” to set {rightLimit, bottomLimit} to size of scroll area 1 in process “Finder”
–> {3840, 1251}

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

■方法2 FinderのDesktop WindowのScroll Areaのsizeを取得

1つのモニタの情報のみ取得。

スクリプト名:displaySize2
–http://piyocast.com/as/archives/1560
tell (do shell script “/usr/sbin/system_profiler SPDisplaysDataType | grep Resolution”) to set {newR, newB} to {word 2 as number, word 4 as number} – get screen size for monitor

–> {1920, 1200}

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

■方法3 com.apple.windowserverの設定ファイルを読み込む方法

1つのモニタだけの情報を取得するようになっているので、そもそも複数のモニタには対応していません。

スクリプト名:displaySize3
–http://piyocast.com/as/archives/1561
return {word 3 of (do shell script “defaults read /Library/Preferences/com.apple.windowserver | grep -w Width”), word 3 of (do shell script “defaults read /Library/Preferences/com.apple.windowserver | grep -w Height”)}

–> {”1920″, “1200″}

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

■方法4 Finderでdesktop Windowのサイズを取得

複数モニタのトータルのデスクトップサイズを取得できます。モニタの位置によっては、マイナスの数値なども返ってきます。

スクリプト名:displaySize4
–http://piyocast.com/as/archives/1562
tell application “Finder”
  get bounds of window of desktop
  
–> {0, -51, 3840, 1200}
end tell

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

とりあえず、いままでのやり方を見直して……とりあえず、system_profilerコマンドを経由して複数のモニタの解像度を取得するようにしてみました。

スクリプト名:モニタの解像度を取得する(複数モニタ対応)
–17インチMacBook Pro本体ディスプレイ
set aRes to retDisplayResolution() of me
–> {{1920, 1200}}

–17インチMacBook Pro本体ディスプレイ(Main) + 外付け24インチモニタ(1920 x 1080)(Sub)の場合
set aRes to retDisplayResolution() of me
–> {{1920, 1200}, {1920, 1080}}

–17インチMacBook Pro本体ディスプレイ(Sub) + 外付け24インチモニタ(1920 x 1080)(Main)の場合
set aRes to retDisplayResolution() of me
–> {{1920, 1200}, {1920, 1080}}

–モニタ解像度を取得。複数モニタ接続時にはリストで列挙
on retDisplayResolution()
  
  
set sRes to do shell script “/usr/sbin/system_profiler SPDisplaysDataType | grep Resolution”
  
  
set sList to paragraphs of sRes
  
set resList to {}
  
  
repeat with i in sList
    set j to contents of i
    
set x1 to word 2 of j as number
    
set y1 to word 4 of j as number
    
set the end of resList to {x1, y1}
  end repeat
  
  
return resList
  
end retDisplayResolution

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

04/21 Safariの最前面のWindowの内容を1枚もののPDFにレンダリングしてデスクトップへ

Safariでオープン中の最前面のWindowの内容をデスクトップにPDF出力するAppleScriptです。

Web関係の仕事をすると、地味に必要になってくる「指定ページを1枚ものの画像にまとめた」PDF。Keynoteの書類などに貼り付けてページ遷移の説明を行ったりするのは、よくある話です。

Safariの最前面のWindowでオープン中のURLを取得し、CLI Webレンダラー「Wkpdf」でPDFにレンダリング出力します。

safario.png

その際に、ファイル名は日付をもとに生成。1枚ものの「長いPDF」として出力するためにWkpdfのレンダリングオプションを指定してページネーションを抑止したり、背景画像の表示をイネーブルにしたりと、見所はそのぐらいで、あとはたいしたことのないあっさりとした処理ばかりです。

Script Menuに入れて使うと便利です。ただ、使用頻度は人によって個人差がありそうなので、実用性がきわめて高い便利なScriptの割には忘れ去られそうな可能性も(自分でも、作っていたことを忘れていました)。

Wkpdf自体のインストールについては、Terminalからコマンドを叩いて行っておく必要がありますが、たいして難しくないので大丈夫でしょう。

スクリプト名:最前面のWindowの内容を1枚もののPDFにレンダリングしてデスクトップへ
tell application “Safari”
  set wCount to count every window
  
if wCount < 1 then
    display dialog “Windowが存在しません” buttons {“OK”} default button 1
    
return
  end if
  
  
tell window 1
    set aInfo to properties
  end tell
  
  
set aDoc to document of aInfo
  
  
tell aDoc
    set aURL to URL
  end tell
  
end tell

set aFileName to “webOut” & (do shell script “date +%Y%m%d%H%M%S”)
renderURLtoPDF(aURL, aFileName) of me

on renderURLtoPDF(aURL, aFileName)
  set s1Text to “cd ~/Desktop && “
  
set outPath to POSIX path of (path to desktop) & aFileName & “.pdf”
  
  
set s2Text to “wkpdf –source “ & aURL & ” –paginate false –print-background –output “ & outPath
  
  
set sAll to s1Text & s2Text
  
do shell script sAll
end renderURLtoPDF

(*
–指定URLの内容をレンダリングしてPDFに書き出す
on renderURLtoPDF(aURL, aFileName)
  set dDir to POSIX path of (path to desktop from user domain)
  set outFile to dDir & aFileName & “.pdf”
  try
    do shell script “cd /usr/local/bin && /usr/local/bin/coral -f PDF -o ” & quoted form of outFile & ” ” & aURL & ” &”
  on error
    return false
  end try
  –return outFile
end renderURLtoPDF
*)

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

03/01 curlでダウンロードv2

curlで指定URLから、指定フォルダにダウンロードを行うAppleScriptです。ファイル名はURLから自動取り出しを行います。

Mac OS X 10.7でURL Access Scriptingが廃止になりました。URL Access Scriptingは、たしかに手軽で便利ではありましたが、非同期ダウンロードができないため、かなり昔から個人的にはcurlを愛用していました。

URL Access ScriptingはMac OS 9の頃からの「負の遺産」(ダウンロード時のファイル名の長さに、いまだに32文字制限があったなど)ともいえる仕様を引きずっており、Appleに作り直す気がなければ消えることを宿命づけられていたものです。

で、一応curlでダウンロードを行うサブルーチンを、少し整備してみましたが……このあたり、人によってずいぶんと趣味が違うはずなので、もっとシンプルなものがいいとか、ゴージャスなものがいいとか、自分の趣味に応じて作り変えるべきだと思います。普段使っている処理では、ダウンロードを非同期で行わせることがよくあるので、そういう方向で強化してもよいと思われます。

スクリプト名:curlでダウンロードv2
–動作確認のためにダイアログ入力しているだけ
set aURL to text returned of (display dialog “URL to download” default answer “” buttons {“OK”} with icon 1)

set dlFolder to path to desktop –ダウンロード先フォルダ(Desktop)
set dRes to downloadFile(aURL, dlFolder, 3600) of me
–POSIX pathが返ってくる。あとでのんびりaliasに変換

–通常ダウンロード
on downloadFile(aURL, outPath, tOut)
  
  
set aFileName to getFNfromURL(aURL) of me
  
if aFileName = false then
    return “”
  end if
  
  
set dlPathStr to POSIX path of outPath
  
set aPOSIXpath to dlPathStr & aFileName
  
  
set dssText to “curl -s “ & aURL & ” -o “ & quoted form of aPOSIXpath
  
try
    with timeout of tOut seconds
      do shell script dssText
    end timeout
  on error
    return false
  end try
  
  
return aPOSIXpath
  
end downloadFile

on getFNfromURL(aURL)
  if aURL ends with “/” then
    –display dialog “与えられたURLの末尾がスラッシュで終了しているため、ファイルではありません。” buttons {”OK”} default button 1
    
return false
  end if
  
set aLen to length of aURL
  
set aR to reverse of (characters of aURL)
  
set aNstr to aR as string
  
set aLoc to offset of “/” in aNstr
  
set aRes to text (aLen - aLoc + 2) thru -1 of aURL
  
return aRes
end getFNfromURL

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

11/09 Time Machineの最新と次回のバックアップ時刻を取得する(Sparse Image用)

By xanadu62

Time Machineの最新バックアップ時刻と次回のバックアップ時刻予定をシステム環境設定を開かずに表示するAppleScriptです。Time Machineに使用している、可変容量のディスクイメージ(Sparse Image)をマウントしているドライブ名を入力すると、それぞれの最新バックアップ時刻と次回のバックアップ時刻予定を表示します。

time2.jpg

HDD名は最初の1回入力するだけでプロパティに保持するので、毎回入力の必要はありません。
今のところ、次回のバックアップ時刻はシステム環境設定が表示する時刻より1分ほど遅れますが、実用上は問題ないのではないかと思います(原因も不明)。
複数のSparse Imageに対応し、若干のバグフィックスも追加しました。

このスクリプトはTime Machineの定期バックアップが行われている場合にのみ有効です。(最新バックアップ時刻にインターバルを加算して次回のバックアップ予定を算出しているため)次回のバックアップ予定を直接取得する手段が見つからないため、このような中途半端な仕様になっています。

スクリプト名:Time Machine BackUp Status_Sparsebundle
【コメント】 Time Machineの最新バックアップ時刻と次回のバックアップ時刻を取得するAppleScript。スパースバンドルイメージをバックアップ先にしているケース用。

Ver.0.12 (2011.10.27)
1.複数のバックアップイメージが見つかった場合に対応。
2.エラーが発生する範囲がバックアップファイルのリスト(ls)コマンド以外にもあったので、処理範囲を広げた。
Ver.0.1(2011.10.24) 初版。

–Time Machineに使っているHDD名を記憶するプロパティ
property TmHddName : “”

on run
  
  
–Time Machineに使っている外付けHDDを取得
  
activate
  
  
if TmHddName is “” then
    
    
display dialog “Time Machineに使っている外付けHDDを接続して、名称を入力してください。” default answer “USB HD 1″ buttons {“やめます”, “入力しました”} default button 2
    
    
set dlogReturned to result
    
  end if
  
  
  
if button returned of the dlogReturned is not “やめます” then
    
    
–Computer Nameをsystem_profilerから取得
    
set ComputerName to do shell script “system_profiler SPSoftwareDataType | grep ‘Computer Name’ | cut -d ‘:’ -f2 | cut -d ‘ ‘ -f2″
    
    
–Time Machineに使っている外付けHDD名をdlogReturnedから取得
    
set TmHddName to text returned of the dlogReturned
    
    
–エラー処理
    
set errFlag to false
    
    
try
      
      
–Time Machineに使っているスパースバンドルイメージ名を取得
      
set TmImgName to do shell script “cd /Volumes/” & quoted form of TmHddName & “; ls | grep ’sparsebundle’”
      
      
set CurrentASTID to AppleScript’s text item delimiters
      
      
set AppleScript’s text item delimiters to return
      
      
set TMImgNameList to every text item of TmImgName
      
      
set AppleScript’s text item delimiters to CurrentASTID
      
      
if length of TMImgNameList is not 1 then
        
        
–set TmImgName to TmImgName
        
        
–else
        
        
choose from list TMImgNameList with prompt “複数のバックアップイメージがあります。一つを選択してください” OK button name “選択” cancel button name “やめる” without empty selection allowed
        
        
set theResult to result
        
        
if theResult is not false then
          
          
set TmImgName to theResult
          
        else
          
          
display dialog “バックアップイメージが選択されなかったので処理を中止します” buttons {“分かりました”} default button 1
          
          
quit of me
          
        end if
        
      end if
      
      
–Time Machineに使っているスパースバンドルイメージをマウントし、マウントされたイメージ名を取得
      
set TmImgAttachedName to do shell script “cd /Volumes/” & quoted form of TmHddName & “; hdiutil attach “ & TmImgName & ” | grep ‘Apple_HFS’ | cut -d ‘/’ -f5″
      
      
–Time Machineに使っているスパースバンドルイメージのパス設定
      
set TMBackUpPath to “/Volumes/” & quoted form of TmImgAttachedName & “/Backups.backupdb/” & quoted form of ComputerName & “/Latest”
      
      
      
do shell script “ls “ & TMBackUpPath
      
    on error
      
      
display dialog “指定されたHDDが見つからないか、HDD上にTime Machineのバックアップがありません。” buttons {“しまった!”} default button 1 giving up after 3
      
      
set TmHddName to “”
      
      
set errFlag to true
      
    end try
    
    
if not errFlag then
      
      
–最新バックアップ時刻の取得
      
set LatestBackUpTimeString to do shell script “ls -lT “ & TMBackUpPath & ” | cut -d ‘ ‘ -f9-13″
      
      
set LatestBackUpTime to EJDate(LatestBackUpTimeString)
      
      
      
–バックアップインターバルの取得と次回のバックアップ時刻生成
      
set StartIntarval to ((do shell script “defaults read /System/Library/LaunchDaemons/com.apple.backupd-auto StartInterval”) as number)
      
      
set NextBackUpTime to LatestBackUpTime + StartIntarval
      
      
–結果の表示
      
activate
      
display dialog (“Time Machineのバックアップ状況は以下の通りです。” & return & return & “最新のバックアップ” & return & LatestBackUpTime as string) & return & return & “次回のバックアップ予定” & return & NextBackUpTime as string buttons {“分かりました”} default button 1
      
      
–Time Machineに使っているスパースバンドルイメージを取り出す
      
do shell script “diskutil eject /Volumes/” & quoted form of TmImgAttachedName
      
      
    end if
    
  end if
  
end run

on EJDate(EDate)
  
  
set TempDlim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to ” “
  
  
set M to text item 1 of EDate
  
  
–日付が一桁の場合と、そうでない場合の分岐処理
  
–一桁の場合、EDateで区切り文字であるスペースが一個増える。つまり、何もない文字が取得されるので、それで分岐を行う。
  
if text item 2 of EDate is “” then
    
    
set D to text item 3 of EDate
    
set T to text item 4 of EDate
    
set Y to text item 5 of EDate
    
  else
    
    
set D to text item 2 of EDate
    
set T to text item 3 of EDate
    
set Y to text item 4 of EDate
    
  end if
  
  
  
set AppleScript’s text item delimiters to TempDlim
  
  
set EMList to {“Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”}
  
  
set JMNum to 1
  
  
repeat with aEM in EMList
    if aEM as text is M then exit repeat
    
set JMNum to JMNum + 1
  end repeat
  
  
return date (Y & “.” & JMNum & “.” & D & “.” & T)
  
end EJDate

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

11/09 Time Machineの最新と次回のバックアップ時刻を取得する(HDD、NAS用)

By xanadu62

Time Machineの最新バックアップ時刻と次回のバックアップ時刻予定をシステム環境設定を開かずに表示するAppleScriptです。

Time Machineに使用している外付けHDD(ローカル接続)またはTime Capsule(以下TC)やNASなどのネットワーク接続しているHDD名を入力すると、それぞれの最新バックアップ時刻と次回のバックアップ時刻予定を表示します。

time1.jpg

HDD名は最初の1回入力するだけでプロパティに保持するので、毎回入力の必要はありません。
今のところ、次回のバックアップ時刻はシステム環境設定が表示する時刻より1分ほど遅れますが、実用上は問題ないのではないかと思います(原因も不明)。
TCやNAS用は、TCやNASに複数のMacのバックアップがある場合には対応していません(やろうと思えばchoose from listでできますが……)。
これらのスクリプトはTime Machineの定期バックアップが行われている場合にのみ有効です。(最新バックアップ時刻にインターバルを加算して次回のバックアップ予定を算出しているため)次回のバックアップ予定を直接取得する手段が見つからないため、このような中途半端な仕様になっています。

スクリプト名:Time Machine BackUp Status
【コメント】 Time Machineの最新バックアップ時刻と次回のバックアップ時刻を取得するAppleScript。ローカルボリュームを直接バックアップ先にしているケース用。

Ver 0.12 (2011.10.23)
1.Time Machineに使っている外付けHDD名をダイアログで入力可能にした。
2.HDD名はpropertyに保持し、毎回入力する必要はなくした。
3.バックアップをスパースバンドルディスクイメージにしたので、その対応処理。
Ver 0.11(2011.10.22) 
1.コンピュータ名を自動取得
2.日付取得部分の微調整(日付が1桁の場合の対応)
3.エラー処理実装
Ver.0.1(2011.10.21) 初版。とりあえず目的は果たしたレベル。

–Time Machineに使っているHDD名を記憶するプロパティ
property TmHddName : “”

on run
  
  
–Time Machineに使っている外付けHDDを取得
  
activate
  
  
if TmHddName is “” then
    
    
display dialog “Time Machineに使っている外付けHDDを接続して、名称を入力してください。” default answer “” buttons {“やめます”, “入力しました”} default button 2
    
    
set dlogReturned to result
    
  end if
  
  
  
if button returned of the dlogReturned is not “やめます” then
    
    
–Computer Nameをsystem_profilerから取得
    
set ComputerName to do shell script “system_profiler SPSoftwareDataType | grep ‘Computer Name’ | cut -d ‘:’ -f2 | cut -d ‘ ‘ -f2″
    
    
–Time Machineに使っている外付けHDD名をdlogReturnedから取得
    
set TmHddName to text returned of the dlogReturned
    
    
–Time Machineに使っている外付けHDDのパス設定
    
set TMBackUpPath to “/Volumes/” & quoted form of TmHddName & “/Backups.backupdb/” & quoted form of ComputerName & “/Latest”
    
    
–エラー処理
    
set errFlag to false
    
    
try
      
      
do shell script “ls “ & TMBackUpPath
      
      
    on error
      
      
display dialog “指定されたHDDが見つからないか、HDD上にTime Machineのバックアップがありません。” buttons {“しまった!”} default button 1 giving up after 3
      
      
set TmHddName to “”
      
      
set errFlag to true
      
    end try
    
    
if not errFlag then
      
      
      
–最新バックアップ時刻の取得
      
set LatestBackUpTimeString to do shell script “ls -lT “ & TMBackUpPath & ” | cut -d ‘ ‘ -f9-13″
      
      
set LatestBackUpTime to EJDate(LatestBackUpTimeString)
      
      
–バックアップインターバルの取得と次回のバックアップ時刻生成
      
set StartIntarval to ((do shell script “defaults read /System/Library/LaunchDaemons/com.apple.backupd-auto StartInterval”) as number)
      
      
set NextBackUpTime to LatestBackUpTime + StartIntarval
      
      
–結果の表示
      
activate
      
display dialog (“Time Machineのバックアップ状況は以下の通りです。” & return & return & “最新のバックアップ” & return & LatestBackUpTime as string) & return & return & “次回のバックアップ予定” & return & NextBackUpTime as string buttons {“分かりました”} default button 1
      
    end if
    
  end if
  
end run

on EJDate(EDate)
  
  
set TempDlim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to ” “
  
  
set M to text item 1 of EDate
  
  
–日付が一桁の場合と、そうでない場合の分岐処理
  
–一桁の場合、EDateで区切り文字であるスペースが一個増える。つまり、何もない文字が取得されるので、それで分岐を行う。
  
if text item 2 of EDate is “” then
    
    
set D to text item 3 of EDate
    
set T to text item 4 of EDate
    
set Y to text item 5 of EDate
    
  else
    
    
set D to text item 2 of EDate
    
set T to text item 3 of EDate
    
set Y to text item 4 of EDate
    
  end if
  
  
  
set AppleScript’s text item delimiters to TempDlim
  
  
set EMList to {“Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”}
  
  
set JMNum to 1
  
  
repeat with aEM in EMList
    if aEM as text is M then exit repeat
    
set JMNum to JMNum + 1
  end repeat
  
  
return date (Y & “.” & JMNum & “.” & D & “.” & T)
  
end EJDate

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

09/25 PDFのページ数をカウントする v3

Mac OS X 10.5以降で、PDFのページ数をカウントするAppleScriptです。

よそで拾ってきたScript(どこで拾ってきたか忘れてしまいました)がサブルーチンになっていなかったので、サブルーチン化したぐらいで書き換えはほとんどやっていないのですが……とりあえずGUI Scripting経由でページ数を取得するとかいう「悪い冗談」みたいな処理ではないので安心感があり、よく使っています。

Mac OS X 10.7以降であればrubyの部分をAppleScriptObjCに書き換えてみてもよさそうですが……わざわざ10.5/10.6で動かないように作ってもしょうがないですし、専用のバンドル形式のランタイムでないと動作しない(AppleScriptエディタ上で記述する)AppleScriptObjCはイマイチではないかと。AppleScriptObjCの記述はさすがにXcode上で行うことがほとんどです。

スクリプト名:PDFのページ数をカウントする v3
set aPDF to choose file of type "com.adobe.pdf"
set pNum to pdfCount(aPDF) of me

–指定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

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

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

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

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

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

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

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

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

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

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

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

(*
プロパティ:

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

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

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

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

07/09 SIGMA Raw画像からメーカーと機種名を取り出す v2

SIGMAのデジカメで撮影したRAW画像ファイル(拡張子「.X3F」)を分析して、内包しているEXIFデータから「メーカー名+機種名」の文字列情報を取り出すAppleScriptです。

本ScriptでSIGMAのRAW画像ファイルを処理すると「SIGMA DP1」などのメーカー名+モデル名の文字列を、取得できなかった場合にはFalseを返します。

写真家の友人から「たまった写真データを機種別にフォルダ分けして、ファイル名の重複があった場合には連番でリネームするAppleScriptを作って〜」と、頼まれて物々交換でAppleScriptを作ったときに、JPEGファイルについてはImage EventsやsipsコマンドでEXIF情報にアクセスできるのですが、肝心のSIGMAのRAWデータだけはなぜかEXIF情報が取得できないことが判明。

インターネット上でデジカメメーカー各社のRAWファイルをかき集め、CANNONの「CR2」、OLYMPUSの「ORF」、富士フィルムの「RAF」、リコーの「DNG」、ニコンの「NEF」などひととおりsipsでEXIF情報にアクセスできることを確認。

SIGMAがマイナーなマニアックなメーカーであるために、Mac OS X 10.6.xが標準でSIGMA RAWの解析機能を持っていないことが原因であることが判明しました。

そこで、仕方なくAppleScriptでSIGMAのRAWファイルを解析。どうせRAW画像ファイルといっても、EXIFの情報を内包していることは予想でき、しかもまともな技術者ならEXIF情報はファイルの先頭の方に置いておくはず。

stringsコマンドで文字列らしきところを文字ダンプ。あっさりと先頭の方にEXIF情報が見つかりました。

さらに、ファイル全体に対して実行すると数十メガバイト分も処理することになって面倒(時間がかかる)なので先頭の数十行分だけしか処理しないようにして試してみました。

他社のRAW画像はおおかたMac OS Xが標準で解析できるので、本AppleScriptはあくまでSIGMAのRAW画像解析専用です。

スクリプト名:SIGMA Raw画像からメーカーと機種名を取り出すテスト v2
set a to choose file with prompt “Please Select Raw File”
set aRes to getCameraNameFromSIGMArawfile(a) of me

–SIGMAのRawファイルを解析してメーカー名+機種名を取り出す
on getCameraNameFromSIGMArawfile(aFile)
  set aPOSIX to POSIX path of aFile
  
  
ignoring case
    if aPOSIX does not end with “.X3F” then
      return false
    end if
  end ignoring
  
  
  
–Raw画像ファイルを文字ダンプして先頭から40行取り出す
  
set sText to “/usr/bin/strings “ & quoted form of aPOSIX & ” | head -n 40″
  
set aRes to do shell script sText
  
  
set aList to paragraphs of aRes
  
set aLen to length of aList
  
  
–Exifっぽい部分をサーチ
  
set aMaker to “”
  
set aMakerModel to “”
  
  
repeat with i from 1 to aLen
    set j to contents of item i of aList
    
if j contains “Exif” then
      set aMaker to contents of item (i + 1) of aList
      
set aMakerModel to contents of item (i + 2) of aList
      
exit repeat
    end if
  end repeat
  
  
–含んでいなかった場合(T_T)
  
if aMaker = “” then
    return false
  end if
  
  
–結果表示
  
return aMakerModel
  
end getCameraNameFromSIGMArawfile

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

07/06 指定のデジカメ画像のExif情報から指定のプロパティを取得する

指定のデジカメ画像のExif情報から指定のプロパティを取得するAppleScriptです。

デジカメ画像のExif情報を取得する場合には、Image Events経由でアクセスすることになりますが……実際に使ってみると、クラッシュしまくるImage Events。AppleScriptの実行をApple純正のアプリケーションが阻害するというあり得ない現象が頻発。

そこで、Image Eventsをアテにしないで、sipsコマンドをAppleScriptから呼び出すことにしてみました。Image Eventsはsipsを呼び出しているAS用インタフェース・アプリケーションですが、この調子ではAppleScriptによる並列処理などを考えるとsipsをダイレクトに呼び出した方がご利益が大きそうです。

指定できるプロパティ値については、Terminal上でsipsコマンドのhelpを見て確認してください。

Special property keys:
all binary data
allxml binary data

Image property keys:
dpiHeight float
dpiWidth float
pixelHeight integer (read-only)
pixelWidth integer (read-only)
typeIdentifier string (read-only)
format string jpeg | tiff | png | gif | jp2 | pict | bmp | qtif | psd | sgi | tga
formatOptions string default | [low|normal|high|best| ] | [lzw|packbits]
space string (read-only)
samplesPerPixel integer (read-only)
bitsPerSample integer (read-only)
creation string (read-only)
make string
model string
software string (read-only)
description string
copyright string
artist string
profile binary data
hasAlpha boolean (read-only)

Profile property keys:
description utf8 string
size integer (read-only)
cmm string
version string
class string (read-only)
space string (read-only)
pcs string (read-only)
creation string
platform string
quality string normal | draft | best
deviceManufacturer string
deviceModel integer
deviceAttributes0 integer
deviceAttributes1 integer
renderingIntent string perceptual | relative | saturation | absolute
creator string
copyright string
md5 string (read-only)

スクリプト名:retExifAttributeData v2
set aFile to choose file
set aRes to retExifAttributeData(aFile, “model”) of me
–> “GR Digital “

set bRes to retExifAttributeData(aFile, “formatOptions”) of me
–> “default”

–SIPSコマンドで指定のデジカメ画像のExif情報(プロパティ)を取得する
on retExifAttributeData(aFile, aParam)
  
  
set aPOSIX to quoted form of POSIX path of aFile
  
  
set aRes to do shell script “sips –getProperty “ & aParam & ” “ & aPOSIX
  
set aList to paragraphs of aRes
  
set anItem to contents of last item of aList
  
  
set colonPos to (offset of “:” in anItem) + 2
  
set eRes to text colonPos thru -1 of anItem
  
  
return eRes
  
end retExifAttributeData

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

04/24 スリープ復帰日時を設定する

スリープからの復帰日時を指定するAppleScriptです。設定に成功するとtrueが、失敗した場合にはfalseが返ります。

シェルのpmsetコマンドで復帰日時を詳細に設定します。AppleScriptObjCで簡単な録音アプリケーションを作成したときに、録音開始時刻までスリープして予約時刻が近くなったらスリープ解除すべく作成したものです。

当初は、Cocoaの呼び出しによってAppleScriptObjCベースで書けないか調べていたのですが、結局Cocoa+Objective-CでもCarbonベースのシステムコールを呼び出しているようで……それだとAppleScriptObjCから呼べないはず(たぶん)。

そこで、他のやり方がないかどうか調査していたところ、あっさりとシェルコマンドでできることが分かったので、通常のAppleScriptで記述してみました。本サンプルプログラム中では、ユーザーのパスワードを空欄に指定していますが、文字定数で記述しておくか、あるいは一度ユーザーに入力を求めてKeychainに記録しておくなどの方法がベストでしょう(2回目以降はKeychainからパスワードを取得)。

スリープからの解除時にパスワードを要求するように設定してあるマシンでは、スリープ復帰時にパスワードの入力を求められます。

pmsetコマンド側で現在時刻との比較を行っており、スリープ復帰日時を「過去」に設定するとエラーになります。また、スリープしていない状態(起きている状態)でスリープ復帰日時を迎えても何も起こりません。動作確認のためには、一度Macをスリープさせることが必要です(メニューから実行するもよし、AppleScriptからSystem Events経由でsleepコマンドを実行するもよし)。また、シャットダウンしてある状態から自動起動することもないはずです。

スクリプト名:スリープ復帰日時を設定する
set aYear to 2011 –年
set aMonth to 4 –月
set aDate to 24 –日

set aHour to 22 –時
set aMinute to 51 –分
set aSecond to 0 –秒

set myAccount to do shell script “whoami” –他に管理者アカウントを指定することも可。その場合にはmyPasswordには管理者のパスワードを指定
set myPassword to “” –パスワード

set pRes to setAwakeDate(aYear, aMonth, aDate, aHour, aMinute, aSecond, myAccount, myPassword) of me

–スリープからの復帰日時を設定する
on setAwakeDate(aYear, aMonth, aDate, aHour, aMinute, aSecond, myAccount, myPassword)
  if myPassword = “” then
    set myPassword to text returned of (display dialog “パスワードを入力してください” default answer “” with hidden answer)
  end if
  
  
–現在のユーザーに管理権限がない場合にはリターン
  
if getUsersPrivileges(myAccount) of me = false then return false
  
  
  
–数値の整形(日付)
  
set tYear to retZeroPaddingText(aYear, 2) of me
  
set tMonth to retZeroPaddingText(aMonth, 2) of me
  
set tDate to retZeroPaddingText(aDate, 2) of me
  
  
–数値の整形(時刻)
  
set tHour to retZeroPaddingText(aHour, 2) of me
  
set tMinute to retZeroPaddingText(aMinute, 2) of me
  
set tSecond to retZeroPaddingText(aSecond, 2) of me
  
  
set tDateStr to (ASCII character 34) & tMonth & “/” & tDate & “/” & tYear & ” “ & tHour & “:” & tMinute & “:” & tSecond & (ASCII character 34)
  
  
try
    set sRes to (do shell script “/usr/bin/pmset schedule wake “ & tDateStr user name myAccount password myPassword with administrator privileges)
  on error erMes
    return erMes –時間を過去に設定したか、あるいは数値の範囲指定エラー。パスワードの指定ミスの可能性もあり得る
  end try
  
  
return true
  
end setAwakeDate

–指定ユーザーの権限を得る(管理者か、それ以外か) 10.4および10.5以降両用
–管理者だとtrueが、それ以外だとfalseが返る
on getUsersPrivileges(aUser)
  set aVer to system attribute “sys2″ –OSメジャーバージョンを取得する(例:Mac OS X 10.6.4→6)
  
  
set current_user to aUser
  
  
if aVer > 4 then
    –Mac OS X 10.5以降の処理
    
set adR to (do shell script “/usr/bin/dsmemberutil checkmembership -U “ & current_user & ” -G admin users”)
    
    
if adR = “user is a member of the group” then
      return true
    else
      return false
    end if
    
  else
    –Mac OS X 10.4までの処理
    
set admin_users to (do shell script “/usr/bin/niutil -readprop . /groups/admin users”)
    
tell (a reference to AppleScript’s text item delimiters)
      set {old_atid, contents} to {contents, ” “}
      
set {admin_users, contents} to {text items of admin_users, old_atid}
    end tell
    
    
if current_user is in admin_users then
      return true
    else
      return false
    end if
  end if
  
end getUsersPrivileges

–数値にゼロパディングしたテキストを返す
on retZeroPaddingText(aNum, aLen)
  set tText to (“0000000000″ & aNum as text)
  
set tCount to length of tText
  
set resText to text (tCount - aLen + 1) thru tCount of tText
  
return resText
end retZeroPaddingText

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

04/10 ドロップされたASをdiff表示 v4

ドロップされたASをFileMergeでビジュアルdiff表示v3のバージョンアップ版です。2つのAppleScriptのファイルをFileMerge(Xcodeをインストールすると一緒に入る)でビジュアルdiff表示します。

GUI Scriptingを用いているため、実行のためにはこれをオンにしておく必要があります。また、本Scriptはアプリケーション形式で保存したうえで実行(AppleScriptのファイルを2つ、ドラッグ&ドロップ)することになります。

スクリプト名:ドロップされたASをdiff表示 v4
on run
  –環境確認を行うべき(書いてない)
  
  
–FileMergeの起動を最初にやっておく
  
if running of application “FileMerge” then –Mac OS X 10.5だか10.6で拡張された属性
    –すでに起動している場合には何もしない
  else
    tell application “FileMerge”
      launch
    end tell
  end if
  
end run

on open fileList
  
  
tell application “Finder”
    set sortedList to sort fileList by creation date –オリジナルはmodification dateだったが……趣味の問題?
  end tell
  
  
set sortedList to sortedList’s reverse
  
  
set oldPath to writeASSourceToTempFolder((sortedList’s item 1) as alias)
  
set newPath to writeASSourceToTempFolder((sortedList’s item 2) as alias)
  
  
do shell script “/usr/bin/opendiff “ & oldPath’s POSIX path’s quoted form & ” “ & newPath’s POSIX path’s quoted form & ” > /dev/null 2>&1 &”
  
  
–指定プロセスでDialogが出て処理が停まっている場合には、強制的にダイアログをクローズして処理続行させる
  
clickFrontDialog(“FileMerge”) of me
  
end open

–AppleScriptのソースを取得してファイルに書き出し
on writeASSourceToTempFolder(macPath)
  set aName to (info for macPath size 0)’s name
  
set tmpPath to ((path to temporary items from system domain) as text) & aName & “__” & (do shell script “/usr/bin/uuidgen”) & “.txt”
  
  
set scptText to do shell script “/usr/bin/osadecompile “ & macPath’s POSIX path’s quoted form
  
  
set accessFile to open for access file tmpPath with write permission
  
set eof of accessFile to 0
  
write scptText to accessFile starting at eof as text
  
close access accessFile
  
  
return tmpPath
end writeASSourceToTempFolder

–表示されるダイアログを乗り越えるためのルーチン(「キャンセル」もしくは「Cancel」ではない方のボタンを押す)
on clickFrontDialog(aProcName)
  –余計なプロセスを起動せずにactivate
  
activateAproc(aProcName) of me
  
  
–ダイアログ検出
  
tell application “System Events”
    tell process aProcName
      if (count every window) 1 then
        if subrole of window 1 = “AXDialog” then
          tell window 1
            set bCount to count every button
            
            
–「キャンセル」もしくは「Cancel」ではない方のボタンを取得
            
repeat with i in {“キャンセル”, “Cancel”} –キャンセルに該当するキーワードのリストを各国語分だけ用意しておく???
              set bList to (every button whose title is not equal to (contents of i))
              
if length of bList is not equal to bCount then
                set aButton to first item of bList
                
tell aButton
                  click
                  
exit repeat
                end tell
              end if
            end repeat
          end tell
        end if
      end if
    end tell
  end tell
end clickFrontDialog

on activateAproc(aName)
  tell application “System Events”
    set aList to every process whose name is aName and visible of it is true
    
    
if length of aList 1 then
      set aProc to first item of aList
      
tell aProc
        set frontmost to true
      end tell
      
    else
      tell application aName to activate
    end if
    
  end tell
end activateAproc

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

04/10 ドロップされたASをdiff表示 v3

ドロップされたASをFileMergeでビジュアルdiff表示のバージョンアップ版です。GUI Scriptingを用いているため、実行のためにはこれをオンにしておく必要があります。

asdiff.jpg

コメント欄でedama2さんに提案していただいたものがv2。これは、TextWranglerを併用しなくてよくなったのと、shell commandを呼び出したあとできちんと終了するように改良したものです。また、一時ファイルのファイル名にオリジナルが何か分かるように作成されている点も有用な改良点だと思います。

ただ……それをそのまま掲載したのでは能がないので、このv3を作成してみました。

(1)日本語入りAppleScriptをFileMergeでオープンした際に表示される「Files are not ASCII」の警告ダイアログを無理やりGUI Scriptingでスキップ

filem1.jpg

(2)FileMergeのactivate時に、なぜか複数のFileMergeが起動されてしまう、という現象が(一時的に)見られたため、起動中のFileMergeのプロセスを特定して、そいつをactivateするようにした
filem2.jpg

(3)ファイル変更日時でソートされていたものを、ファイル作成日でソート

といった変更を加えています(3つ目のは単なる個人的な趣味の問題です)。

AppleScriptのDiff表示は、AppleScriptエディタの標準機能として搭載してほしいぐらいのものです。

ほかにも、複数のAppleScript間の使用サブルーチン比較表の作成プログラム、構文要素を考慮した置換(変数名のみ置換、定数のみ置換 など)などもすでにAppleScriptで書いたものがあり、日常的に使っていると「なぜこれが標準搭載されていないのか?」と不思議に思えるほどです。

osadecompileコマンドについては、ほとんど使ったことがなかったのですが……こういう分析系のタスクに用いるのがよいのだろうと思っています。あとは、AppleScriptの実行前に「危険な内容」が含まれていないかを事前にチェックするために利用する、ぐらいでしょうか。

スクリプト名:ドロップされたASをdiff表示 v3
on open fileList
  
  
tell application “Finder”
    set sortedList to sort fileList by creation date –オリジナルはmodification dateだったが……趣味の問題?
  end tell
  
  
set sortedList to sortedList’s reverse
  
  
set oldPath to writeASSourceToTempFolder((sortedList’s item 1) as alias)
  
set newPath to writeASSourceToTempFolder((sortedList’s item 2) as alias)
  
  
do shell script “/usr/bin/opendiff “ & oldPath’s POSIX path’s quoted form & ” “ & newPath’s POSIX path’s quoted form & ” > /dev/null 2>&1 &”
  
  
–指定プロセスでDialogが出て処理が停まっている場合には、強制的にダイアログをクローズして処理続行させる
  
clickFrontDialog(“FileMerge”) of me
  
end open

–AppleScriptのソースを取得してファイルに書き出し
on writeASSourceToTempFolder(macPath)
  set aName to (info for macPath size 0)’s name
  
set tmpPath to ((path to temporary items from user domain) as text) & aName & “__” & (do shell script “/usr/bin/uuidgen”) & “.txt”
  
  
set scptText to do shell script “/usr/bin/osadecompile “ & macPath’s POSIX path’s quoted form
  
  
set accessFile to open for access file tmpPath with write permission
  
set eof of accessFile to 0
  
write scptText to accessFile starting at eof as text
  
close access accessFile
  
  
return tmpPath
end writeASSourceToTempFolder

–表示されるダイアログを乗り越えるためのルーチン(「キャンセル」もしくは「Cancel」ではない方のボタンを押す)
on clickFrontDialog(aProcName)
  –余計なプロセスを起動せずにactivate
  
activateAproc(aProcName) of me
  
–tell application aProcName to activate
  
–tell application “FileMerge” to activate
  
  
–ダイアログ検出
  
tell application “System Events”
    tell process aProcName
      if (count every window) 1 then
        if subrole of window 1 = “AXDialog” then
          tell window 1
            set bCount to count every button
            
            
–「キャンセル」もしくは「Cancel」ではない方のボタンを取得
            
repeat with i in {“キャンセル”, “Cancel”} –キャンセルに該当するキーワードのリストを各国語分だけ用意しておく???
              set bList to (every button whose title is not equal to (contents of i))
              
if length of bList is not equal to bCount then
                set aButton to first item of bList
                
tell aButton
                  click
                  
exit repeat
                end tell
              end if
            end repeat
          end tell
        end if
      end if
    end tell
  end tell
end clickFrontDialog

on activateAproc(aName)
  tell application “System Events”
    set aList to every process whose name is aName and visible of it is true
    
if length of aList 1 then
      set aProc to first item of aList
      
tell aProc
        set frontmost to true
      end tell
    end if
  end tell
end activateAproc

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

04/02 ドロップされたASをFileMergeでビジュアルdiff表示

FileMergeで2つのAppleScriptをdiff表示するAppleScriptです。

使用時には、アプリケーション形式で保存してドロップレットとして使います。保存したドロップレットに2つのAppleScriptのファイルをドラッグ&ドロップすると、osadecompileコマンドでAppleScriptをUTF-8のテキストに変換し、さらにTextWranglerでBOMつきのUTF-8に変換します。変換が終わったら、FileMergeでdiff表示します。

実行にはFileMerge(Xcodeをインストールすると入ってくる)と、TextWrangler(Bare Bones Softwareからフリーで配布されています)が必要です。Mac OS X 10.6上でのみ実行可能です。

「指定のAppleScriptをosadecompileでUTF-8のテキストに変換して、さらにUTF-8(BOMつき)にTextWranglerで変換して、パスを返す」AppleScriptを作ったあとに、FileMergeのフィルタからAppleScriptを呼び出してみたのですが、パスを返したらパスの文字列同士でdiffをとられてしまうし、ファイル内容をstdoutに返したら元のダメな表示に逆戻り。この方法ではまったくダメでした。

そんなときに、FileMergeをコマンドラインから呼び出せるはずだと思い立ち、いろいろ探してみると「opendiff」という名前で呼び出せるという情報に行き当たりました

そこで、これまでに作ったさまざまなサブルーチンをごてごてとコピー&ペーストでまとめつつ、ちょっとだけコードを書いて希望の動作を行わせることに成功。

diff3.jpg

日本語が入ったAppleScriptのプログラムを、きちんとdiff表示できています。

diff4.jpg

ただ、opendiffを呼び出したあとにドロップレットがそのまま実行から帰ってこないのですが……このあたりはまだ検討の余地がありそうです。

もともと、FileMergeのフィルタとして作り始めた本AppleScriptですが、結局AppleScriptのドロップレットにするのであれば、なんとなくAppleScriptエディタをAppleScriptからコントロールして操作するとか、そういう方法のほうがTextWranglerを引っ張り出してくる必要もなかったんじゃないかと思うものです。

まあでも、トータル半日ぐらいで希望の動作が実現できたのでよしとしましょう。実行後にドロップレットから結果が返ってこないで無反応になってしまいますが、強制終了させてやってください。

diff5.jpg

スクリプト名:ドロップされたASをdiff表示
on run
  –環境チェック
  
  
(*


  本来はここで、TextWranglerやFileMerge(opendiff)などが実行環境に存在するかどうかチェックすべき
  実行するOSのバージョンもチェックしておいたほうがよい
  
  *)

end run

on open fileList
  –2つのファイルがドロップされているかを判定。フォルダとかバンドルがドロップされる可能性は否定できないものの、ノーチェック
  
–スクリプトバンドルがきたら、AppleScriptエディタ経由で内容を取得する???
  
if length of fileList is not equal to 2 then
    display dialog “かならず1組(2つ)のファイルをドロップしてください。” buttons {“OK”} default button 1 with icon 1
    
return
  end if
  
  
–ファイル作成日でソート(古いものから新しいものへ)
  
set aliasRes to sortAliasByCreationDate(fileList) of me
  
  
set {file1Path, file2Path} to aliasRes
  
  
–UTF8 with BOMに変換
  
set file1PosixPath to decompileASandSaveAsUTF8withBOM(file1Path) of me
  
set file2PosixPath to decompileASandSaveAsUTF8withBOM(file2Path) of me
  
  
do shell script “/usr/bin/opendiff “ & quoted form of file1PosixPath & ” “ & quoted form of file2PosixPath & “&”
  
end open

–指定のAppleScriptをosadecompileでUTF-8のテキストに変換して、さらにUTF-8(BOMつき)にTextWranglerで変換して、パスを返す
on decompileASandSaveAsUTF8withBOM(aFile)
  set aTmp to ((path to temporary items from system domain) as string) & (do shell script “/usr/bin/uuidgen”) & “.txt”
  
set aTmpFile to POSIX path of aTmp
  
  
do shell script “/usr/bin/osadecompile “ & quoted form of POSIX path of aFile & ” > “ & quoted form of aTmpFile
  
  
  
–TextWranglerを起動して不可視に
  
tell application “TextWrangler” to launch
  
  
tell application “System Events”
    tell process “TextWrangler”
      set visible to false
    end tell
  end tell
  
  
  
–TextWranglerでUTF8のファイルをUTF8(With BOM)に変換する
  
tell application “TextWrangler”
    set aDoc to open file aTmp reading as UTF8 file
    
set distEncoding to encoding of aDoc
    
    
–BOMなしのUTF8だったらBOMつきUTF8に変更する
    
–”Unicode (UTF-8)”
    
–”Unicode (UTF-8, with BOM)”
    
if distEncoding = “Unicode (UTF-8)” then
      set encoding of aDoc to “Unicode (UTF-8, with BOM)”
    end if
    
    
save aDoc add to recent list boolean false –最近使ったファイルの一覧に登録しない
    
close aDoc without saving
    
  end tell
  
  
return aTmpFile
  
end decompileASandSaveAsUTF8withBOM

–ファイル作成日でソート(古いものから新しいものへ)
on sortAliasByCreationDate(aliasList)
  
  
tell application “Finder”
    set aList to {}
    
    
repeat with i in aliasList
      set j to contents of i
      
set the end of aList to {j, creation date of j}
    end repeat
    
    
set cList to shellSortListAscending(aList, 2) of me –で作成日時でソート
    
    
    
set aliasList to {}
    
set fnCount to 1
    
repeat with i in cList
      set j to contents of (item 1 of i)
      
      
set the end of aliasList to j
    end repeat
  end tell
  
  
return aliasList
  
end sortAliasByCreationDate

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(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 shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
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

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

04/02 指定のAppleScriptをosadecompileでUTF-8のテキストに変換して、さらにUTF-8(BOMつき)にTextWranglerで変換して、パスを返す

指定のAppleScriptをosadecompileコマンドでUTF-8のテキストに変換し、さらにTextWranglerでBOMつきのUTF-8に変換して返すAppleScriptです。

AppleのXcodeと一緒にインストールされる「FileMerge」は2つのファイルの間の相違点をピックアップするdiff系のツールとしては、Mac OS X上では最高に出来がよく、使いやすいものだと(個人的に)思っています。

diff1.jpg

これを使ってAppleScriptのプログラムの相違点・変更点をリストアップできると便利ですが、残念ながらFileMergeが扱えるのはテキストファイルであり、AppleScriptのファイルをそのまま渡すことはできません。

そこで、Mac OS X標準装備のosadecompileコマンドを用いてAppleScriptのファイルをテキストファイルに変換し、それをFileMergeに渡せるとよいのではないかと考えました。

FileMergeにはファイルタイプごとに前処理を行うフィルタを設定でき、osadecomileコマンドを呼び出せるとよさそうです。TextWranglerでなくとも、UTF-8をBOMつきのUTF-8に変換するコマンドラインツールがあれば、そちらのほうがよさそうですが……さて、あるものなのか……???

diff2.jpg

ところが……このAppleScriptの中に日本語の文字列が入っているとうまく行きません。

osadecompileが出力する結果はUTF-8。しかし、そのままではFileMergeが読み込むことはできません。iconvを使ってUTF-8からさまざまな文字コードに変換してFileMergeに渡してもダメ。

さらに調べてみると、UTF-8でもBOMつきのUTF-8に変換すると、FileMergeが処理できることが分かりました。iconvでは役に立たなかったので、BareBones Softwareからフリー配布されているテキストエディタTextWranglerで変換するようにしてみました。

まだ機能を詰め切れていませんが、これで指定のAppleScriptの内容をFileMergeで比較できるように変換できています。

あとは、変換したファイルをいかにFileMergeに渡すかですが…………いっそ、このAppleScriptを/usr/local/binあたりに放り込んでおいて、FileMergeのフィルタから呼び出せばいいのかもしれません。

スクリプト名:指定のAppleScriptをosadecompileでUTF-8のテキストに変換して、さらにUTF-8(BOMつき)にTextWranglerで変換して、パスを返す
set aFile to choose file
set aRes to decompileASandSaveAsUTF8withBOM(aFile) of me

–指定のAppleScriptをosadecompileでUTF-8のテキストに変換して、さらにUTF-8(BOMつき)にTextWranglerで変換して、パスを返す
on decompileASandSaveAsUTF8withBOM(aFile)
  set aTmp to ((path to temporary items from system domain) as string) & (do shell script “/usr/bin/uuidgen”) & “.txt”
  
set aTmpFile to POSIX path of aTmp
  
  
do shell script “/usr/bin/osadecompile “ & quoted form of POSIX path of aFile & ” > “ & quoted form of aTmpFile
  
  
  
–TextWranglerを起動して不可視に
  
tell application “TextWrangler” to activate
  
  
tell application “System Events”
    tell process “TextWrangler”
      set visible to false
    end tell
  end tell
  
  
  
–TextWranglerでUTF8のファイルをUTF8(With BOM)に変換する
  
tell application “TextWrangler”
    set aDoc to open file aTmp reading as UTF8 file
    
set distEncoding to encoding of aDoc
    
    
–BOMなしのUTF8だったらBOMつきUTF8に変更する
    
–”Unicode (UTF-8)”
    
–”Unicode (UTF-8, with BOM)”
    
if distEncoding = “Unicode (UTF-8)” then
      set encoding of aDoc to “Unicode (UTF-8, with BOM)”
    end if
    
    
save aDoc add to recent list boolean false –最近使ったファイルの一覧に登録しない
    
close aDoc without saving
    
  end tell
  
  
return aTmp as alias
  
end decompileASandSaveAsUTF8withBOM

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

02/27 指定のテキストをAppleScriptとしてコンパイルし、実行可能なアプレットとして出力する

指定のテキストをAppleScriptとしてosacompileコマンドでコンパイルし、実行可能なアプレット(ドロップレット)として指定パスに保存するAppleScriptです。

テキストから実行可能なアプリケーションを生成する場合には、AppleScriptエディタ(スクリプトエディタ)を外部からAppleScriptでコントロールするやり方をよく使ってきました。手あかがつくほど使った方法なので、信頼性も高く、あえて同じことを別のやり方でできるように準備しておく必要は(本来は)ありません。

しかし、Appleが将来的な方針を明らかにしない以上、何らかの方針転換を行った場合に備えておくことも必要です。たいていの(重要な)処理は複数のやり方で実現できるように、下調べをしてあります。その一環として、このサブルーチンを作ることにしました。

Mac OS X 10.6でosacompileコマンドに実行ファイル作成機能が付加されたため、これを使って指定のテキストからバイナリを作成できるようにしてみました。

休みの日にテレビを見ながら作っていたので、ひどく散漫なコードになってしまい、無駄な処理なども残っているかもしれません。ただ、期待どおりの動作は行うようなのでとりあえずこんなもので。

osacompileコマンドのアプレットの出力パス指定ではずいぶん手間どらされました。出力先をフルパスで指定してもうまく動作せず、cdコマンドとの合わせ技でなんとか。

スクリプト名:指定のテキストをAppleScriptとしてコンパイルし、実行可能なアプレットとして出力する
set asText to “on run
tell application \”Finder\”
beep
display dialog \”test\”
beep
end tell
end run

on open(aPath)

end

set asFile to choose file name
set asRes to makeASexecutable(asText, asFile) of me

if asRes is not equal to true then
  display dialog asRes
  
return
end if

–指定のテキストをAppleScriptとしてコンパイルし、実行可能なアプレットとして出力する
on makeASexecutable(aText, outPathAlias)
  
  
–出力先のaliasからファイル名のみ撮り出す
  
set aStr to outPathAlias as string
  
set fnRes to getLastLayerOfDirData(aStr) of me
  
  
–指定のファル名に拡張子が適切に付いていない場合には補う
  
if fnRes does not end with “.app” then
    set fnRes to fnRes & “.app”
  end if
  
  
–テンポラリフォルダ内のテキストファイルに書き出す
  
set aFileName to (do shell script “uuidgen”) & “.applescript”
  
set tmpAppName to (do shell script “uuidgen”) & “.app”
  
  
set aTempFol to path to temporary items folder from system domain
  
set aTempFolPosix to (POSIX path of aTempFol) & aFileName
  
  
set aTempPathText to (aTempFol as string) & aFileName
  
write_to_file(aText, aTempPathText, false) of me
  
  
–ファイルキャッシュをHDDにシンクロさせる
  
–do shell script “sync”–> 実行しなくても大丈夫だった
  
  
–osacompileコマンドでアプレットを生成
  
set sText to “cd “ & (quoted form of POSIX path of aTempFol) & ” && osacompile -s -x -o “ & fnRes & ” “ & aFileName
  
try
    –コンパイル時にアプリケーションの起動を待ったりする可能性があるので、待ち時間を1時間に設定
    
with timeout of 3600 seconds
      do shell script sText
    end timeout
  on error erMes
    return erMes
  end try
  
  
  
–テンポラリに書き込んだTextのAppleScriptを消す
  
try
    do shell script “rm -f “ & quoted form of aTempFolPosix
  end try
  
  
  
–生成したアプレットを指定のフォルダに移動する
  
set origAppPath to POSIX path of ((aTempFol as string) & fnRes)
  
set destPath to POSIX path of getOnlyFolder(aStr) of me & fnRes
  
  
try
    do shell script “mv -f “ & quoted form of origAppPath & ” “ & quoted form of destPath
  on error erMes
    return erMes
  end try
  
  
return true
  
end makeASexecutable

–テキストで与えられたパスデータから、フォルダのみを返す
on getOnlyFolder(a)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {“:”}
  
set aList to text items of a
  
set bList to items 1 thru -2 of aList
  
set aText to bList as string
  
set AppleScript’s text item delimiters to curDelim
  
set aText to aText & “:”
  
return aText
end getOnlyFolder

–テキストで与えられたパスデータから、最終階層のテキストのみを返す
on getLastLayerOfDirData(aData)
  set aColon to “:” as Unicode text
  
copy aData to bData
  
if bData ends with “:” then
    set bData to text 1 thru -2 of bData
  end if
  
  
set aLen to length of bData
  
set rData to reverse of (characters of bData)
  
set rData to rData as string
  
set dPos to offset of aColon in rData
  
set resData to text (aLen - dPos + 2) thru -1 of bData
  
return resData
  
end getLastLayerOfDirData

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

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

02/08 選択したファイルをQuickLook

選択したファイルをQuickLookで拡大表示するAppleScriptです。

The MacTipper Blogで紹介されていました。

ql1.jpg

最初、System Events経由でスペースキーのキーコードを送ってみたりしていたのですが、うまくいかず……検索してみたらThe MacTipper Blogを見つけた次第。

スクリプト名:選択したファイルをQuickLook
【コメント】 The MacTipper Blogより
http://www.mactipper.com/2009/03/quicklook-and-applescript.html
set a to choose file
quicklook(a) of me

on quicklook(the_file)
  set the_path to quoted form of (POSIX path of the_file) as string
  
do shell script (“qlmanage -p “ & the_path)
end quicklook

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

12/29 指定フォルダ容量をMB単位で返す_v2

Finder上で選択したフォルダの容量をMB単位で返すAppleScriptです。

容量が大きい場合に結果が指数表示になってしまっていたため、duコマンドのパラメータを変更してみました。

また、Mac OS X 10.6からはディスク容量を1024ではなく1000で繰り上がるように計算するようになったので、本サブルーチン(=duコマンド)の結果とFinderの結果は異なります。

概算のために用意したので自分としてはこれで大丈夫ですが、気になる方はduコマンドからバイト単位で結果を取得して計算するとよいでしょう。

スクリプト名:指定フォルダ容量をMB単位で返す_v2
tell application “Finder”
  set aSel to selection –as alias list–10.4.11で効かなかった。びっくりした
  
if aSel = {} then
    display dialog “Finder上で何も選択されていません。” buttons {“OK”} default button 1 with icon 1
    
return
  end if
  
  
set aaSel to first item of aSel
  
set aaSel to aaSel as alias
  
end tell

set aMBsize to getFolderSizeByMB(aaSel) of me

–指定フォルダのalias以下の容量をMBで返す
on getFolderSizeByMB(aFolAlias)
  set aDir to (POSIX path of aFolAlias)
  
set aDir to quoted form of aDir
  
  
try
    –set aRes to do shell script “du -k -d 0 ” & aDir
    
set aRes to do shell script “du -m -d 0 “ & aDir
  on error
    return
  end try
  
  
set aList to words of aRes
  
set aSize to (first item of aList)
  
  
return aSize
  
end getFolderSizeByMB

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

12/29 指定フォルダ容量をMB単位で返す

指定フォルダの容量を求めるAppleScriptです。

単純にシェルのduコマンドを呼び出しているだけで、しかも指定するのはフォルダでなくてもよいのですが、フォルダ内の容量取得を目的として作成したものです。

Finder上で何らかのフォルダを選択した状態で本AppleScriptを実行してください。

ファイルサーバー上の指定フォルダを処理する際に、一定容量以上のサイズのフォルダの場合には「大きすぎる」とメッセージを出して処理を行わないようにすることを検討。その判断のために本サブルーチンを作成しました。

微妙に、duコマンドの「Kバイト」(1024で桁上がり)とMac OS X 10.6上のKバイト(1000で桁上がり)の間に差があって、Finder上の結果と異なる数字になるようですが、概算で取得できればよいのでこんなものだと思っています。

スクリプト名:指定フォルダ容量をMB単位で返す
tell application “Finder”
  set aSel to selection –as alias list–10.4.11で効かなかった。びっくりした
  
if aSel = {} then
    display dialog “Finder上で何も選択されていません。” buttons {“OK”} default button 1 with icon 1
    
return
  end if
  
  
set aaSel to first item of aSel
  
set aaSel to aaSel as alias
  
end tell

set aMBsize to getFolderSizeByMB(aaSel) of me

–指定フォルダのalias以下の容量をMBで返す
on getFolderSizeByMB(aFolAlias)
  set aDir to (POSIX path of aFolAlias)
  
set aDir to quoted form of aDir
  
  
try
    set aRes to do shell script “du -k -d 0 “ & aDir
  on error
    return
  end try
  
  
set aList to words of aRes
  
set aSize to (first item of aList) as number
  
  
set mSize to aSize / 1024
  
return mSize
  
end getFolderSizeByMB

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

12/06 Finder上で選択中のフォルダをdiskimageに(パスワード指定つき)

Finderで選択中のフォルダをdiskimageファイル(ディスクイメージ)にするAppleScriptです。diskimageファイルには任意のパスワードを指定できます。

スクリプト名:Finder上で選択中のフォルダをdiskimageに(パスワード指定つき)
property aPassword : "piyopiyo"

tell application "Finder"
  set aSel to selection as alias list
  
if aSel = {} then
    display dialog "Finder上で何も選択されていません。" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
set aaSel to first item of aSel
  
set fRes to detectFolder(aaSel) of me
  
  
if fRes = false then
    display dialog "Finder上でフォルダが選択されていません。" buttons {"OK"} default button 1 with icon 1
    
return
  end if
end tell

tell application "Finder"
  set fn to name of aaSel
end tell

set aDir to (POSIX path of aaSel)
set aDir to quoted form of aDir

set fp2 to do shell script "dirname " & aDir
set fp2 to fp2 & "/"

set aCMD to "printf ‘" & aPassword & "’ | hdiutil create -encryption -srcfolder " & aDir & " " & (quoted form of (fp2 & fn & ".dmg"))
set fp3 to do shell script aCMD

–aliasがFolderかどうか判定
on detectFolder(aSelFol)
  set aInfo to info for aSelFol as alias
  
tell application "Finder"
    try
      set aFol to kind of aInfo
    on error
      return false
    end try
  end tell
  
  
return (aFol = "フォルダ")
end detectFolder

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

12/05 Finder上で選択中のフォルダ内のすべてのファイルの作成日、変更日をcurrent dateに変更

Finder上で選択中のフォルダ内のすべてのファイル/フォルダの作成日、変更日を現在時刻に変更するAppleScriptです。

実行のためには、Xcode Toolsのインストールが必要です。

Script Menuに入れて、サンプルのXcodeプロジェクトの作成日/修正日の日付をまとめて変更するために作ったものです。

スクリプト名:Finder上で選択中のフォルダ内のすべてのファイルの作成日、変更日をcurrent dateに変更
set aCurDate to current date
set aDateStr to makeMMDDYYYYhhmmssStr(aCurDate) of me

tell application "Finder"
  set aSel to selection as alias list
  
if aSel = {} then
    display dialog "何も選択されていません" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
set aSelFol to first item of aSel
  
  
set fRes to detectFolder(aSelFol) of me
  
  
if fRes of me = false then
    display dialog "フォルダが選択されていません" buttons {"OK"} default button 1 with icon 1
    
return
  end if
  
  
tell folder aSelFol
    set aList to entire contents as alias list
  end tell
  
end tell

repeat with i in aList
  set aFres to detectFolder(i) of me
  
–if aFres = false then
  
changeCreationDate(i, aDateStr) of me
  
–end if
end repeat

–作成日と修正日を変更する
on changeCreationDate(aFile, aDateStr)
  set bP to POSIX path of aFile
  
  
try
    do shell script "/usr/bin/SetFile -d " & quoted form of aDateStr & " " & quoted form of bP –作成日
    
do shell script "/usr/bin/SetFile -m " & quoted form of aDateStr & " " & quoted form of bP –修正日
  end try
  
end changeCreationDate

–aliasがFolderかどうか判定
on detectFolder(aSelFol)
  set aInfo to info for aSelFol as alias
  
tell application "Finder"
    try
      set aFol to kind of aInfo
    on error
      return false
    end try
  end tell
  
  
return (aFol = "フォルダ")
end detectFolder

–DateオブジェクトからMM/DD/YYYY hh:mm:ssの形式の文字列を返す
on makeMMDDYYYYhhmmssStr(aDate)
  –Dateオブジェクトから各要素を取り出す
  
set yStr to (year of aDate) as string
  
set mStr to (month of aDate as number) as string
  
set dStr to (day of aDate) as string
  
set hhStr to (hours of aDate) as string
  
set mmStr to (minutes of aDate) as string
  
set ssStr to (seconds of aDate) as string
  
  
–桁数を合わせる
  
set y2Str to retZeroPaddingText(yStr, 4) of me
  
set m2Str to retZeroPaddingText(mStr, 2) of me
  
set d2Str to retZeroPaddingText(dStr, 2) of me
  
set hh2Str to retZeroPaddingText(hhStr, 2) of me
  
set mm2Str to retZeroPaddingText(mmStr, 2) of me
  
set ss2Str to retZeroPaddingText(ssStr, 2) of me
  
  
return (m2Str & "/" & d2Str & "/" & y2Str & " " & hh2Str & ":" & mm2Str & ":" & ss2Str)
end makeMMDDYYYYhhmmssStr

–数値にゼロパディングしたテキストを返す
on retZeroPaddingText(aNum, aLen)
  set tText to ("0000000000" & aNum as text)
  
set tCount to length of tText
  
set resText to text (tCount - aLen + 1) thru tCount of tText
  
return resText
end retZeroPaddingText

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

11/05 フォント情報をSystem Profiler経由で取得する

動作が不安定なうえにバグの多いFont Bookを使わず、/usr/sbin/System_Profilerを呼び出して、フォント関連の情報のみをXML形式で書き出し、System Events経由で書き出したXML(plist)ファイルをparseしてレコード×フォント分のリストを取得するAppleScriptです。

OS側が認識しているフォントの情報については、このSystem_Profilerで安全に取得することができます。ただし、あくまで情報の取得だけです。

処理速度については、インストールされているフォントの数に比例するため、遅いとも速いともいいがたいところですが、安全かつ確実に情報が取れるところがメリットです。

なお、本AppleScriptの末尾でリストを表示させるようにしていますが、このようにして処理結果を確認しようとすると、実際の処理時間にくわえてAppleScriptエディタで結果を表示するために前処理する時間が余計にかかるため、とりあえず適当なファイルに書き出しておいて、あとで内容を確認するのがよいでしょう。

スクリプト名:フォント情報をSystem Profiler経由で取得する
global pItems, pItems_r, fStore, fStore_r

–フォント情報をSystem_Profiler経由でXML形式(plist)で取得する
set aPLIST to do shell script “System_Profiler SPFontsDataType -xml”

–plistをファイルに書き出す
set aTmp to (path to temporary items from system domain) as string
set aFileName to do shell script “uuidgen”
set aTmpPath to aTmp & aFileName & “.plist”
write_to_file_UTF8(aPLIST, aTmpPath, false) of me

–書き出したplistファイルを読み込む
tell application “System Events”
  set plif to property list file aTmpPath
end tell

tell application “System Events”
  
  
set pList to every property list item of plif
  
set ppList to every property list item of contents of first item of pList
  
  
set pItems to value of every property list item of first item of ppList
  
set pItems_r to a reference to pItems
  
  
set fStore to {}
  
set fStore_r to a reference to fStore
  
  
repeat with i in pItems
    
    
set aPitem to contents of i
    
set the end of fStore_r to aPitem
    
  end repeat
end tell

fStore

–UTF8形式でファイル書き込み
on write_to_file_UTF8(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
    try
      close access file target_file
    end try
    
return false
  end try
end write_to_file_UTF8

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

08/12 現在のユーザーの権限を調べる v2

現在のユーザーの権限を調べ、管理者権限があるかないかを調べて返すAppleScriptです。

Mac OS X 10.4まではnetinfo系のコマンドで調べられたのですが、10.5でnetinfoが廃止になり……代わりのコマンドで取得できるようにしてみました。10.4までと10.5および10.6以降をサポートできるようにしてみました。

現在のユーザーに管理者権限がある場合にはtrueが、ない場合にはfalseが返ってきます。

スクリプト名:現在のユーザーの権限を調べる v2
set aPriv to getCurUsersPrivileges() of me

–現在実行中のユーザーの権限を得る(管理者か、それ以外か) 10.4および10.5以降両用
–管理者だとtrueが、それ以外だとfalseが返る
on getCurUsersPrivileges()
  set aVer to system attribute "sys2" –OSメジャーバージョンを取得する(例:Mac OS X 10.6.4→6)
  
  
set current_user to (do shell script "whoami") –実行中のユーザー名を取得
  
  
if aVer > 4 then
    –Mac OS X 10.5以降の処理
    
set adR to (do shell script "/usr/bin/dsmemberutil checkmembership -U " & current_user & " -G admin users")
    
    
if adR = "user is a member of the group" then
      return true
    else
      return false
    end if
    
  else
    –Mac OS X 10.4までの処理
    
set admin_users to (do shell script "/usr/bin/niutil -readprop . /groups/admin users")
    
tell (a reference to AppleScript’s text item delimiters)
      set {old_atid, contents} to {contents, " "}
      
set {admin_users, contents} to {text items of admin_users, old_atid}
    end tell
    
    
if current_user is in admin_users then
      return true
    else
      return false
    end if
  end if
  
end getCurUsersPrivileges

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

08/06 指定フォルダ内にエイリアス書類を作成

指定フォルダ以下にあるXcodeプロジェクト書類をspotlightでリストアップし、別途指定した出力先フォルダにエイリアス書類を出力するAppleScriptです。

「com.apple.xcode.project」の部分を書き換えれば、お好きな書類をリストアップ可能です。

alias1.jpg

エイリアス書類をAppleScriptから作成することは、(自分は)めったにないのですが……たとえば、何らかの条件に合致するファイル群(あるいは多量のパス情報)を抽出したとして、そのエイリアス書類を特定のフォルダに作成して入れておくと、いちいちテキストを見ながらFinder上で指定フォルダに移動してファイルをオープン……といった煩雑な処理をしなくて済みます。

ただ、実戦でもそこまでやることは少ないので、エイリアス書類をAppleScriptから作成するケースは非常に少ないのですが、やり方を再確認しておこうと考え、ちょっと書いてみました。

エイリアス書類を作成するのに、「make new alias file」で作成しています。以前(Classic Mac OS時代)には異なる記法だったような気がするのですが、もはやClassic Mac OSを使うこともないので気にしないことに。

エイリアス書類の作成コマンドと、エイリアス書類のファイル名が重複してしまった場合の回避処理がみどころです。ただし、ファイル名重複時の回避処理はそれほど気合いが入っておらず、同名のファイルが256個以上出力される場合にはエラーになります。そこは、256以上の数値に変えてみるか……抜本的に重複回避処理を見直してみたほうがよいでしょう。

スクリプト名:指定フォルダ内にエイリアス書類を作成
set aFol to choose folder with prompt "エイリアスファイルを出力するフォルダを選択"
set bFol to choose folder with prompt "Xcode書類を探すフォルダを選択"

set bList to getFileListWithSpotLight("kMDItemContentType", "com.apple.xcode.project", bFol) of me –Xcodeのファイルをクリエータコードで検索
repeat with i in bList
  set aFolStr to aFol as string
  
makeAliasFileInAFolder(i, aFolStr) of me
end repeat

–指定フォルダ内に指定名称のエイリアス書類を作成する
on makeAliasFileInAFolder(anAlias, outFol)
  tell application "Finder"
    set aName to name of anAlias
    
tell folder outFol
      if file aName exists then
        set c to makeUniqueName(outFol, aName) of me
      end if
    end tell
    
    
set newalias to make new alias at folder outFol to anAlias
    
set name of newalias to aName
  end tell
end makeAliasFileInAFolder

–指定フォルダ下に存在できる、指定名称+数字の名前の(かぶらない)ファイル名を作成
on makeUniqueName(aFolder, origName)
  set pureFileName to retFileNameWithoutExt(origName) of me
  
set anExt to retExtNameFromFilenameStr(origName) of me
  
  
tell application "Finder"
    tell folder aFolder
      repeat with i from 1 to 256
        if not (exists of file (pureFileName & " " & (i as string) & "." & anExt)) then
          return (pureFileName & " " & (i as string) & "." & anExt)
        end if
      end repeat
    end tell
  end tell
end makeUniqueName

–ファイル名から拡張子を外す
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

–ファイル名文字列から拡張子のみ取得する
on retExtNameFromFilenameStr(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 (fLen - anOffset + 1) thru -1 of fileNameStr
  
return fRes
end retExtNameFromFilenameStr

–指定階層下で、指定クリエータコードのファイルを取得(Mac OS X 10.4.x以上で動作)
on getFileListWithSpotLight(aMetaDataItem, aParam, startDir)
  set sDirText to quoted form of POSIX path of startDir
  
set shellText to "mdfind ‘" & aMetaDataItem & " == \"" & aParam & "\"’ -onlyin " & sDirText
  
try
    set aRes to do shell script shellText
  on error
    return {}
  end try
  
set pList to paragraphs of aRes
  
set aList to {}
  
repeat with i in pList
    set aPath to POSIX file i
    
set aPath to aPath as alias
    
set the end of aList to aPath
  end repeat
  
return aList
end getFileListWithSpotLight

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

07/21 ファイル作成日付を変更する

ファイル作成日付および最終変更日を変更するAppleScriptです。

AppleScriptからFinderをコントロールしても作成日/変更日は変更できないため、shellコマンドを呼び出してくることになります。

Developper Toolsをインストールすると一緒に入ってくるSetFileコマンド(/usr/bin/SetFile)を呼び出します。Developper Toolsをインストールしていない環境ではSetFileがそもそも存在していないので、本Scriptを実行しても日付の変更は行われません。

昔に撮りためたQuickTake 100の写真データをコンバートしようとして、QuickTake PICTをGraphic Converterでオープンできることを確認したのですが、作成日付に当時のもの(15年ぐらい昔)を残しておきたいと思い立ち、作成日の変更方法を調べてみた次第です。

スクリプト名:ファイル作成日付を変更する
set a to choose file with prompt "作成日付の情報をピックアップするファイルを選択してください"
tell application "Finder"
  set aInfo to info for a
  
set creDate to creation date of aInfo
end tell
set aDateStr to makeMMDDYYYYhhmmssStr(creDate) of me

set b to choose file with prompt "日付情報を変更するファイルを選択してください"
set bP to POSIX path of b

try
  do shell script "/usr/bin/SetFile -d " & quoted form of aDateStr & " " & quoted form of bP –作成日
  
do shell script "/usr/bin/SetFile -m " & quoted form of aDateStr & " " & quoted form of bP –修正日
end try

–DateオブジェクトからMM/DD/YYYY hh:mm:ssの形式の文字列を返す
on makeMMDDYYYYhhmmssStr(aDate)
  –Dateオブジェクトから各要素を取り出す
  
set yStr to (year of aDate) as string
  
set mStr to (month of aDate as number) as string
  
set dStr to (day of aDate) as string
  
set hhStr to (hours of aDate) as string
  
set mmStr to (minutes of aDate) as string
  
set ssStr to (seconds of aDate) as string
  
  
–桁数を合わせる
  
set y2Str to retZeroPaddingText(yStr, 4) of me
  
set m2Str to retZeroPaddingText(mStr, 2) of me
  
set d2Str to retZeroPaddingText(dStr, 2) of me
  
set hh2Str to retZeroPaddingText(hhStr, 2) of me
  
set mm2Str to retZeroPaddingText(mmStr, 2) of me
  
set ss2Str to retZeroPaddingText(ssStr, 2) of me
  
  
return (m2Str & "/" & d2Str & "/" & y2Str & " " & hh2Str & ":" & mm2Str & ":" & ss2Str)
end makeMMDDYYYYhhmmssStr

–数値にゼロパディングしたテキストを返す
on retZeroPaddingText(aNum, aLen)
  set tText to ("0000000000" & aNum as text)
  
set tCount to length of tText
  
set resText to text (tCount - aLen + 1) thru tCount of tText
  
return resText
end retZeroPaddingText

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

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

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

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

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

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

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

05/07 指定のtarアーカイブを展開する

指定のtarアーカイブを展開(元に戻す)するAppleScriptです。

スクリプト名:指定のtarアーカイブを展開する
set a to choose file with prompt ".tarファイルを指定してください"
set b to extractTar(a) of me

–指定tarアーカイブを展開する
on extractTar(a)
  set anAlias to a as alias
  
  
tell application "Finder"
    set aParent to parent of anAlias
    
set aFileName to name of anAlias
  end tell
  
  
set aParent to aParent as alias
  
  
set preCMD to "cd " & quoted form of POSIX path of aParent
  
set tarCMD to "tar xf " & quoted form of (aFileName)
  
  
try
    set aRes to do shell script (preCMD & " && " & tarCMD)
  on error
    return false
  end try
  
  
set newName to text 1 thru -5 of aFileName –展開後の、".tar"を除去した名前
  
set extractFol to (aParent as string) & newName & ":"
  
  
try
    return (extractFol as alias)
  on error
    return false
  end try
  
end extractTar

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

05/05 指定フォルダをtarでまとめる

指定フォルダをtarでまとめるAppleScriptです。

指定のフォルダを渡すと、指定フォルダと同じ階層に指定フォルダ名でtarアーカイブを作成します。オリジナルのフォルダは削除せず、そのまま残しておきます。エラー時にはfalseが、正常動作時にはtarアーカイブへのaliasが返ります。

一般的には、フォルダをアーカイブする場合にはZipコマンドで圧縮してまとめたほうが手っ取り早いところですが、対象のファイルが巨大(数百Mバイトオーダー、数Gバイトオーダー)である場合などは「圧縮」するのが大変です。さらに、そもそも圧縮されているデータ(ムービーや画像)なので「再圧縮する意味がない」という場合もあります。

そこで、圧縮せずに単に「1ファイルにまとめる」tarアーカイブを用いた方が便利なケースがあります。本AppleScriptはそのために作成したものです。ただし、tarアーカイブにまとめるファイルには、日本語のファイル名やフォルダ名を使った場合に「展開時にファイル名を戻せない」といった問題(そもそもtarがそういう仕様)があるため、英数字のみのファイル名を使用するなどの注意が必要です。

tarは、語源が「Tape ARchive」であるとおり、HDDの内容をテープストリーマ(バックアップ装置)に転送するために、複数のファイルをまとめて1つのtarファイルにして磁気テープに書き出すためのものです。ExabyteのDATストリーマーなどをUNIXワークステーションで使ったことはありますが、さすがにMac OS X環境で磁気テープストリーマを使ったことはありません、、、

スクリプト名:指定フォルダをtarでまとめる
set a to choose folder
set b to tarFolder(a) of me

–指定フォルダをtarでまとめる
on tarFolder(a)
  set anAlias to a as alias
  
  
tell application “Finder”
    set aParent to parent of anAlias
    
set aFolName to name of anAlias
  end tell
  
  
set aParent to aParent as alias
  
  
set preCMD to “cd “ & quoted form of POSIX path of aParent
  
set tarCMD to “tar cvf “ & quoted form of (aFolName & “.tar”) & ” “ & quoted form of (aFolName & “/”)
  
  
try
    set aRes to do shell script (preCMD & ” && “ & tarCMD)
  on error
    return false
  end try
  
  
  
tell application “Finder”
    set tarAlias to ((aParent as string) & aFolName & “.tar”) as alias
    
set tarEx to exists of tarAlias
  end tell
  
  
if tarEx = true then
    return tarAlias
  else
    return false
  end if
end tarFolder

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

04/13 指定容量のRAMディスクを作る

指定容量(単位=Mバイト)&指定名称のRAMディスクを作成するAppleScriptです。

Web上で漫然とRAMディスクについて調べていたら、shell経由で作成する方法が紹介されていたので……AppleScriptですべてのプロセスを自動化して作成できるようにまとめてみました。

ramdisk.jpg

ただ……これだけ便利に使えるようにしておいてナニですが……RAMディスクを作れてもそんなにスピード面でメリットが感じられなかったというか、それってディスクイメージをマウントしている状態とさほど変わらないんじゃないか、といった疑問でいっぱいです。

イジェクトする際には、フツーにFinder上から「取り出す」コマンドを実行するか、AppleScriptから、

tell application “Finder”
 eject disk “piyoWolf”
end tell

のように実行すればイジェクトできます。

スクリプト名:指定容量のRAMDISKを作る
set mSize to 128
set newRamDiskName to “RAMDISK2″
set ramRes to makeRAMDISK(mSize, newRamDiskName) of me

–指定容量のRAMDISKを作る(あんまりメリットないけど)
on makeRAMDISK(aSize, aName)
  –指定名称のドライブがすでに存在していたらfalseを返す
  
tell application “Finder”
    set dRes to exists of disk aName
    
if dRes = true then return false
  end tell
  
  
–RAMDISKを作ってみる
  
set sText1 to (“hdid -nomount ram://$((2048*” & aSize as string) & “))”
  
set sRes1 to do shell script sText1
  
  
set sText2 to “newfs_hfs -v “ & aName & ” “ & sRes1
  
do shell script sText2
  
  
set sText3 to “diskutil mount “ & sRes1
  
do shell script sText3
  
  
return true
  
end makeRAMDISK

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