Archive for 3月, 2013

2013/03/30 CSVデータを読み込んでリスト化

CSV(camma-separated values)ファイルを読み込んで、parseしてリスト型変数(2次元配列)にして返すAppleScriptです。

“aaa”, “bbb”, “1000″
“ccc”, “ddd”, “999″

という内容が書かれたCSVファイル(テキストファイル)が存在していた場合に、これを本Scriptで読み込むと……

{{”aaa”, “bbb”, “1000″}, {”ccc”, “ddd”, “999″}}

と解釈されます。

CSV書き出しのルーチンはよく使うのですが、CSVの読み込みは作ったことがなかったことが判明。あわててその場で作りました。

何らかのアプリケーションの書類をオープンして、CSV書き出しを行い……CSV書き出しした内容を読み込んで、正しくすべてのデータが書き出されたかを検証してエラー報告を行う、というAppleScriptを書いたときの副産物です。

結局、CSV読み込みを行うのではなく「CSV書き出しするデータをそのままいただく」だけで処理できたので、実験段階では必要なプログラムであったものの、本番運用時には不要に……。

スクリプト名:CSVデータを読み込んでリスト化
set aFile to choose file

set csvList to readAndParseCSV(aFile) of me

on readAndParseCSV(aFile)
  
  
script spdCSV
    property aaList : {}
    
property bbList : {}
    
property ccList : {}
  end script
  
  
set aaList of spdCSV to {}
  
set bbList of spdCSV to {}
  
set ccList of spdCSV to {}
  
  
  
set aaList of spdCSV to read aFile
  
set bbList of spdCSV to paragraphs of aaList of spdCSV
  
  
repeat with i in bbList of spdCSV
    set aTmp to “{” & i & “}”
    
    
try
      set aTmpList to run script aTmp
      
if aTmpList is not equal to {} then
        set the end of ccList of spdCSV to aTmpList
      end if
    on error
      
    end try
    
  end repeat
  
  
return contents of (ccList of spdCSV)
  
end readAndParseCSV

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

2013/03/27 自然言語による相対日付指定v14

2013/03/27 1Dリストのスイープ

与えられたリスト型変数(1次元配列)内のヌルの項目を除去する(スイープする)AppleScriptです。

入れ子のリスト(2次元配列)をスイープするルーチンは本Blog開設当初に掲載していたのですが、もっと簡単な1次元配列のスイープは(書き捨てレベルなので)掲載していませんでした。

スクリプト名:1Dリストのスイープ
set aList to {“07-203″, “07-202″, “07-201″, “07-200″, “07-199″, “07-198″, “07-197″, “07-196″, “07-195″, “07-194″, “07-193″, “07-192″, “07-215″, “07-214″, “07-213″, “07-212″, “07-211″, “”, “07-210″, “07-209″, “07-208″, “07-207″, “07-206″, “07-205″, “07-204″, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”}

set bList to listSweep1D(aList) of me
bList
–> {”07-203″, “07-202″, “07-201″, “07-200″, “07-199″, “07-198″, “07-197″, “07-196″, “07-195″, “07-194″, “07-193″, “07-192″, “07-215″, “07-214″, “07-213″, “07-212″, “07-211″, “07-210″, “07-209″, “07-208″, “07-207″, “07-206″, “07-205″, “07-204″}

–リスト項目のうち、ヌルの項目はリストから除去する(1-demensionのList)
on listSweep1D(aList)
  set bList to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not equal to “” then
      set the end of bList to j
    end if
  end repeat
  
return bList
end listSweep1D

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

2013/03/27 1Dリスト中のシーケンシャルサーチ

リスト型変数(1次元配列)のシーケンシャルサーチを行うAppleScriptです。

基礎的なルーチンですが、自然と呼び出し回数が多くなる種類の処理なので、他のソーティングルーチンを参考に高速化してみました。10万件(正確には99,999件)のリストをすべて走査するのに要する時間は1秒以下(MacBook Pro Retina MID 2012/2.66GHz Core i7)です。

テストのために、99,999件のデータを作成して一番うしろの項目をサーチさせています。

スクリプト名:1Dリスト中のシーケンシャルサーチ
script spd
  property aList : {}
end script

–計測用テストデータ作成部分
set aList of spd to {}
repeat with i from 1 to 99999
  set the end of aList of spd to i as string
end repeat

display dialog "Start!!"

–サーチ実行および計時部分
set sTime to current date
set a to "99999"

set aRes to retIndexNumInArray(aList of spd, a) of me

set eTime to current date

set eP to eTime - sTime
display dialog (eP as string)
aRes

–1Dimensionリスト中のシーケンシャルサーチ
on retIndexNumInArray(aList, aTarget)
  script obj
    property list : aList
  end script
  
  
–set obj’s list to aList
  
  
set aCount to 1
  
set hitF to false
  
  
repeat with i in obj’s list
    set j to contents of i
    
if aTarget = j then
      return aCount
    end if
    
    
set aCount to aCount + 1
  end repeat
  
  
if hitF = false then return 0
end retIndexNumInArray

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

2013/03/26 10.8でdate文のトリッキーな記述を回避する

「OS X 10.8のdateに強烈なバグ」の話の続報です。

OS X 10.8になって正常に動作しなくなった「自然言語による相対日付指定v13」サブルーチンの調査中に出くわした不具合点とその回避方法について解説します。

結論からいえば、date文のトリッキーな記述を行っていた箇所があり、そこが問題になっていました。素直に書くようにすれば、機能を回復できます。

他のCJK言語環境(中国語や韓国語)においても問題が発生するのか興味深いところです。

スクリプト名:10.8でdate文のトリッキーな記述を回避する
–10.8で問題になる書き方
set tmp4 to "2013/2/1"
set ret2Date to date "0:0:0" of (date tmp4)
–> date "1999年12月31日金曜日 0:00:00"

–10.8で問題にならない書き方
set tmp4 to "2013/2/1"
set ret2Date to date (tmp4 & " 0:0:0")
–> date "2013年2月1日金曜日 0:00:00"

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

2013/03/25 OS X 10.8のdateに強烈なバグ

10.8になって、「自然言語による相対日付指定v13」などの高度な処理を行うカレンダー計算プログラムが正しい値を返さなくなったことに気付いていたのですが、これがあまりに巨大なプログラムなので、問題の所在がどこにあるのか調査できずにいました。

確認してみたところ、どうやらOS X 10.8のdate文に強烈なバグが存在していることが分りました。確認はOS X 10.8.3(Build 12D78)にて行っています。

スクリプト名:10.8のdateバグ
set a to “2013年2月1日金曜日 0:00:00″
set b to date a
–> date “1999年12月31日金曜日 0:00:00″

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

date文で曜日が書かれた日付テキストをdateオブジェクトに変換すると、すべて「1999年12月31日金曜日 0:00:00」になってしまうというものです。

さらに、AppleScriptエディタ上でdate文のあとにダイレクトに日付文字列を書いて指定し、コンパイル(構文確認)を行うとあからさまに1999年12月の日付に変わってしまいます。

ase11.png
▲文字入力中。このあと、コンパイル(構文確認)を実行すると……

ase21.png
▲1999年12月の日付に勝手に書き換えられてしまう!

念のため、手元のMac OS X 10.6.8および10.7.5の環境で同様の内容をテストしてみましたが、これらの環境では問題は発生していません。

問題の回避策は、

 (1)dateに与える日付文字列に曜日を含めないこと
 (2)current dateでdateオブジェクトを作成しておいて、そのyear, month, dayなどのプロパティを個別に設定して返すサブルーチンを用いること
 (3)dateに与える日付文字列に日本語の文字列を含めないこと
 (4)曜日の文字列だけ英語で書くこと

などが考えられます。

スクリプト名:10.8のバグの一時的な回避策1
set a to “2013年2月1日” –曜日を含めない
set b to date a
–> date “2013年2月1日金曜日 0:00:00″

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

スクリプト名:10.8のバグの一時的な回避策2
set aDate to makeDateObj(2013, 2, 1, 0) of me
–> date “2013年2月1日金曜日 0:00:00″

on makeDateObj(aYear, aMonth, aDay, aTimeNum)
  set a to current date
  
set year of a to (aYear as number)
  
set month of a to (aMonth as number)
  
set day of a to (aDay as number)
  
set time of a to aTimeNum
  
return a
end makeDateObj

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

スクリプト名:10.8のバグの一時的な回避策3
set a to “2013/2/1 0:00:00″ –日本語の日付文字列を渡さない
set b to date a
–> date “2013年2月1日金曜日 0:00:00″

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

スクリプト名:10.8のバグの一時的な回避策4
set a to “2013年2月15日Friday” –曜日だけ英語で書く
set b to date a
–> date “2013年2月15日金曜日 0:00:00″

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

2013/03/23 write命令実行時のファイル書き込みテキストエンコーディングが10.8.3で修正された

AppleScriptのWrite命令(ファイルへの書き込みを実行)にオプションを付けることでファイル書き込み時のテキストエンコーディングを変更することができるようになっています。

このテキストエンコーディングは、OS X 10.8で変更になりましたが……結論からいえばこれは「バグ」でした。AppleScriptエンジニアリングチーム側では手の出しようのないCore OS側のチームの配下で起こった問題のようで、もともと縦割りで官僚的な傾向の強いAppleの社内において発生しやすいタイプのバグといえます。

AppleScriptでデータのファイル保存を行うのに、一番安全なのは内部形式であるリストやレコード形式で書き込んだり、あるいは仕様が安定しているplistファイルに保存することでしょう。テキストファイルに書き込む場合には、慣習的にUTF8で書いてきました。

テキストエンコーディングを指定してのファイル書き込みを行う場合や、テキストエンコーディングを自動判別してテキストを読み込むため「だけ」にテキストエディタを併用するユーザーがいるようですが、これらのうちオープンソースで作られているものもあるため、ファイルの読み書き部分のみ抜き出して利用することも検討してもよいかもしれません。そこまでしなくても、AppleScript命令拡張書類である各種OSAXを併用するというやり方もあります。

file.png

※ 表中のCPU名は、動作検証を行ったマシンの搭載CPU。

スクリプト名:ファイル書き込み(無指定時→MacJapanese or SJIS、言語環境依存)
–書き込み先のパスを指定
set aPath to (path to temporary items from user domain) as string
set aTargFile to aPath & (do shell script “uuidgen”)

set aStr to “あいうえお”

–環境依存(日本語環境ではShift_JISだかMacJapanese?、英語だとASCII)
write_to_file(aStr, aTargFile, false) of me
set aPOSIXpath to quoted form of POSIX path of aTargFile
set aRes to do shell script “/usr/bin/hexdump “ & aPOSIXpath

do shell script “/bin/rm -f “ & aPOSIXpath
aRes

–> (10.8.2)0000000 82 a0 82 a2 82 a4 82 a6 82 a8
–> (10.8.3)0000000 82 a0 82 a2 82 a4 82 a6 82 a8 –Build 12D78

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

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

スクリプト名:ファイル書き込み(as string指定時→MacJapanese or SJIS、言語環境依存)
–書き込み先のパスを指定
set aPath to (path to temporary items from user domain) as string
set aTargFile to aPath & (do shell script “uuidgen”)

set aStr to “あいうえお”

–環境依存(日本語環境ではShift_JISだかMacJapanese?、英語だとASCII)
write_to_file(aStr, aTargFile, false) of me
set aPOSIXpath to quoted form of POSIX path of aTargFile
set aRes to do shell script “/usr/bin/hexdump “ & aPOSIXpath

do shell script “/bin/rm -f “ & aPOSIXpath
aRes

–> (10.8.2)0000000 82 a0 82 a2 82 a4 82 a6 82 a8
–> (10.8.3)0000000 82 a0 82 a2 82 a4 82 a6 82 a8 –Build 12D78

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

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

スクリプト名:ファイル書き込み(as Unicode text指定時→UTF-16BE
–書き込み先のパスを指定
set aPath to (path to temporary items from user domain) as string
set aTargFile to aPath & (do shell script “uuidgen”)

set aStr to “あいうえお”

–UTF-16BE
write_to_file_asUTF16BE(aStr, aTargFile, false) of me
set aPOSIXpath to quoted form of POSIX path of aTargFile
set aRes to do shell script “/usr/bin/hexdump “ & aPOSIXpath

do shell script “/bin/rm -f “ & aPOSIXpath
aRes

–> (10.8.2)0000000 ff fe 42 30 44 30 46 30 48 30 4a 30 ———WARNING!!!!
–> (10.8.3)0000000 30 42 30 44 30 46 30 48 30 4a –Build 12D78

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

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

スクリプト名:ファイル書き込み(as «class ut16»指定時→UTF-16LE(BOM付き)
–書き込み先のパスを指定
set aPath to (path to temporary items from user domain) as string
set aTargFile to aPath & (do shell script “uuidgen”)

set aStr to “あいうえお”

–UTF-16LE(BOM付き)
write_to_file_asUTF16LEwithBOM(aStr, aTargFile, false) of me
set aPOSIXpath to quoted form of POSIX path of aTargFile
set aRes to do shell script “/usr/bin/hexdump “ & aPOSIXpath

do shell script “/bin/rm -f “ & aPOSIXpath
aRes

–> (10.8.2) 0000000 30 42 30 44 30 46 30 48 30 4a ———WARNING!!!!
–> (10.8.3) 0000000 ff fe 42 30 44 30 46 30 48 30 4a 30 –Build 12D78

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file_asUTF16LEwithBOM(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 as «class ut16» 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_asUTF16LEwithBOM

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

スクリプト名:ファイル書き込み(as «class utf8»指定時→UTF-8)
–書き込み先のパスを指定
set aPath to (path to temporary items from user domain) as string
set aTargFile to aPath & (do shell script “uuidgen”)

set aStr to “あいうえお”

–UTF-8
write_to_file_asUTF8(aStr, aTargFile, false) of me
set aPOSIXpath to quoted form of POSIX path of aTargFile
set aRes to do shell script “/usr/bin/hexdump “ & aPOSIXpath

do shell script “/bin/rm -f “ & aPOSIXpath
aRes

–> (10.8.2)0000000 e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a
–> (10.8.3)0000000 e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a –Build 12D78

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file_asUTF8(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 as «class utf8» 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_asUTF8

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

2013/03/17 IPアドレスがプライベートIPアドレスかどうかチェック

与えられたIPアドレスが「プライベートIPアドレス」かどうかをチェックするAppleScriptです。

なにやら、ひたすら「すでに存在している」雰囲気が濃厚で作るのがためらわれる種類のScriptですが、喫茶店で音楽を聞きながら5分ぐらいで作ったので、気にしないことにしましょう。

IPアドレスの文字列をピリオドでparseして、条件文で判定するという実に面白味のない処理内容です。

ただし、用途は割と凝ったもので……ストリーミングラジオのタイマー録音内容を、mp3データに変換してRSSを生成して、ローカルWebサーバーのコンテンツディレクトリにコピーして、ローカルPodcastとして配信して自動でiPhoneにシンクロさせるAppleScriptのために作ったものです。

将来的に配布するかどうかも分りませんが、これがそのまま一般公開されているWebサーバーで動いてしまうと、さすがに配布元の責任を問われかねない状況に陥るので、せめて処理前にIPアドレスをチェックしてプライベートIPアドレスで運用されているものかどうか確認するためのものです。この用途においては、プライベートIPチェック機能は実に重要です。

# ただ、IPv6ネイティブの環境で運用しているネットワークは使っていないので、よく分らない、、、、

スクリプト名:IPアドレスがプライベートIPアドレスかどうかチェック
set ipAddress to “192.168.0.1″
set aRes to chkIPaddressIsPrivate(ipAddress) of me
–> true(プライベートIPアドレスでない場合にはfalseが返る)

–与えられたIPアドレスがプライベートIPアドレスかどうかチェック
on chkIPaddressIsPrivate(ipAddress)
  –数字の文字だけで構成されているかどうかチェック
  
set aRes to detectContainsOnlyNumChar(ipAddress) of me
  
if aRes = false then return false
  
  
–ピリオドをデリミタとして各桁に分ける
  
set ipAdrList to parseByDelim(ipAddress, “.”) of me
  
set aLen to length of ipAdrList
  
if aLen is not equal to 4 then return false
  
  
set {ip1, ip2, ip3, ip4} to ipAdrList
  
try
    set ip1 to ip1 as integer
    
set ip2 to ip2 as integer
    
set ip3 to ip3 as integer
    
set ip4 to ip4 as integer
  on error
    return false
  end try
  
  
if ip1 = 10 then
    –Class A
    
return true
  else if ip1 = 172 then
    –Class B
    
if ip2 16 and ip2 32 then
      return true
    end if
  else if ip1 = 192 then
    –Class C
    
if ip2 = 168 then
      return true
    end if
  end if
  
  
return false
  
end chkIPaddressIsPrivate

–与えられた文字列を、指定デリミタ文字でparseしてリストにして返す
on parseByDelim(aData, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set dList to text items of aData
  
set AppleScript’s text item delimiters to curDelim
  
return dList
end parseByDelim

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

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

2013/03/17 miで文字置換v2

Macのテキストエディタ「mi」でオープン中のテキストファイルの文字置換を行うAppleScriptのサンプルです。

miの新しいバージョン3.0betaが出たので、AppleScript系の機能をチェックしていたところ……以前に掲載したmiの文字置換ルーチンの内容を見て、

  「何を無駄な処理をしているんだろう?」

と気付き、書き換えたものです。

mi1.png

mi2.png

以前に掲載したものは、1行ごとに取り出して、行ごとに置換していました(←バカ)。AppleScriptのtext item delimiterの性質を考えると、大量のデータを一気に置換するように組まないとスピードが出ないわけで……データを小分けにして置換してもぜんぜん意味がありません。

たぶん、テキストの行数が増えれば増えるほど所要時間が変わってくるはずです。

mi 3.0betaについては、まだまだ作成途上という印象で……AppleScript系の機能についても、まだ各種オブジェクトからのプロパティを取得できないとか、大幅に刷新されたGUIに対するオブジェクトの定義があるんだかないんだか分らなかったり、というところです。

miのライバルは、フリー配布されていながら機能豊富なTextWraglerだと思います。

差分表示機能を強化しているあたり、このTextWranglerやらJeditやらを意識していると思いますが、最近Windowsから移行してきたユーザーが必要としていると思われるCP932のテキストのサポートあたりで差別化すべきではないかと。

スクリプト名:miで文字置換v2
script spd
  property inList : missing value
  
property outList : missing value
end script

set targStr to “ぴよ” –置換対象文字列
set repStr to “ぷよ” –置換後文字列

set inList of spd to {}
set outList of spd to {}

–最前面のドキュメントから本文を取得
tell application “mi”
  tell document 1
    set (inList of spd) to content
  end tell
end tell

–置換処理
set (outList of spd) to repChar((inList of spd), targStr, repStr) of me

–最前面のドキュメント本文に置換結果を戻す
tell application “mi”
  tell document 1
    set content to (outList of spd)
  end tell
end tell

–文字置換ルーチン
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

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

2013/03/16 指定のバンドルIDのアプリを指定数起動 v1

海外のWebで、openコマンド経由で指定プロセスを複数起動する、という記事がありました

do shell scriptコマンド1行の、素材そのままの内容だったので、ちょっと手を入れて指定数のプロセスを起動できるようにしてみました。

元記事は「Run Multiple Instances Of Any App On Your Mac With An AppleScript」となっていましたが、実際に試してみたところすべて可能というわけではありませんでした。

multi1.png

multi2.png

multi3.png

たとえば、Photoshop CS6とかIllustrator CS5は複数起動できましたが、InDesign CS3は複数起動できませんでした。このあたり、バージョンの問題なのか、アプリの性質の問題なのかよく分らないのですが…………。

追記:InDesign CS6で複数起動を試みましたが、複数起動はブロックされていました

複数のプロセスから設定ファイル(1つあるいは複数)を共有してしまうはずなので、はっきり言って(排他制御とか、書き込んだデータ内容が矛盾したりしないか)「大丈夫なの?」という感想しか持っていないのですが、とりあえず起動できることは起動できています。

最大の問題は、複数起動してもAppleScriptからそれらを個別にコントロールできないので、AppleScript的にはほとんど無意味だということです。残念。あと、複数起動するとそれなりにメモリを喰われます。

スクリプト名:指定のバンドルIDのアプリを指定数起動 v1
launchMultipleINstance(“com.adobe.photoshop”, 3) of me

–指定のバンドルIDのアプリを指定数起動
on launchMultipleINstance(appBundleID, n)
  
  
tell application “Finder”
    set anAppFile to (application file id appBundleID) as alias
  end tell
  
  
set anAppFilePath to anAppFile as string
  
if anAppFilePath ends with “:” then
    set anAppFilePath to text 1 thru -2 of anAppFilePath
  end if
  
  
set appPosixPath to quoted form of POSIX path of anAppFilePath
  
  
repeat n times
    do shell script “/usr/bin/open -n “ & appPosixPath
  end repeat
  
end launchMultipleINstance

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

2013/03/14 入れ子のリストの昇順、降順ソート(超高速版)

入れ子のリスト(2次元配列)を超高速でソート(shell sort)するAppleScriptです。

以前に投稿していただいたScriptを元に、2次元配列にも対応できるよう書き換えたものです。

Core i7 2.66GHzのMacBook Pro Retina mid 2012(OS X 10.8.2)上で、1万レコードの昇順/降順ソートに1秒程度。1.6GHzのCore i5搭載のMacBook Air mid 2011で2秒程度。PowerMac G5 2.7GHz Dual Core(Mac OS X 10.4.11)上で3秒程度です。

上記MacBook Pro上で3万レコードの入れ子のリスト(2次元配列)ソートに5秒程度に対して、入れ子でないリスト(1次元配列)のソート結果が4秒程度。次元が1次元増えてこの程度の変化であれば、なかなかいい結果が出ていると思います。

スクリプト名:入れ子のリストの昇順、降順ソート
–check code 3

script spd
  property aList : {}
  
property bList : {}
end script

–Initialize
set aList of spd to {}
set bList of spd to {}

repeat 10000 times
  set the end of aList of spd to {“aaa”, random number from 1000 to 9999}
end repeat

set sTime to current date

set bList of spd to shellSortListDescending(aList of spd, 2) of me

set eTime to current date

display dialog (eTime - sTime) as string

(*
–check code 1
set aList to {{”abc”, 9}, {”bcd”, 7}, {”ccc”, 3}, {”avc”, 4}, {”dcc”, 5}}
set bList to shellSortListDescending(aList, 2) of me
–> {{”abc”, 9}, {”bcd”, 7}, {”dcc”, 5}, {”avc”, 4}, {”ccc”, 3}}

–check code 2
set cList to shellSortListAscending(aList, 2) of me
–> {{”ccc”, 3}, {”avc”, 4}, {”dcc”, 5}, {”bcd”, 7}, {”abc”, 9}}
*)

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(aSortList, aKeyItem)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) > item aKeyItem of temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDescending(aSortList, aKeyItem)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) < item aKeyItem of temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortListDescending

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

2013/03/13 アプリケーションのクラッシュレポートダイアログの表示切り換え

Mac OS X上でアプリケーションがクラッシュした際に、クラッシュレポートをAppleに送信するCrash Reporterの動作を変更するAppleScriptです。

アプリケーションのコントロールを行っていて、その対象がある程度の頻度でクラッシュすることが予想される場合(Adobe CSアプリケーションのことですね)、操作対象のアプリケーションのプロセスの存在確認などを行うことになりますが、同時に……クラッシュレポートの送信を禁止しておきたいところです。

そのような場合に、本ルーチンを呼び出してくラッシュレポートダイアログ表示の抑止などの指示を行うことになります。実際の内容は、/usr/bin/defaultsコマンドをdo shell scriptコマンド経由で呼び出しているだけですが、地味に有用です。

スクリプト名:アプリケーションのクラッシュレポートダイアログの表示切り換え
–クラッシュレポートダイアログを表示しない
set aRes to disableCrashReportDialog(false) of me

–クラッシュレポートダイアログを表示する
set aRes to disableCrashReportDialog(true) of me

–クラッシュレポーターの設定状態を取得する
set aRes to disableCrashReportDialog(“”) of me

–アプリケーションのクラッシュレポートダイアログの表示切り換え
on disableCrashReportDialog(aParam)
  if aParam = false then
    –クラッシュレポートダイアログを表示しない
    
set aRes to (do shell script “/usr/bin/defaults write com.apple.CrashReporter DialogType none”)
  else if aParam = true then
    –クラッシュレポートダイアログを表示する
    
set aRes to (do shell script “/usr/bin/defaults write com.apple.CrashReporter DialogType crashreport”)
  else
    –クラッシュレポーターの設定状態を取得する
    
set aRes to (do shell script “/usr/bin/defaults read com.apple.CrashReporter”)
  end if
  
  
return aRes
end disableCrashReportDialog

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

2013/03/13 各テキストフレームの衝突ペアを作成

InDesign CS3(CS3以降であればtellブロックのアプリ名の書き換えのみでOK)のドキュメント上に存在するテキストフレームのうち、重なっているもののペアリスト(衝突リスト)を作成するものです。

indn1.png

こんなふうに(↑)テキストフレームが重なり合いつつ配置されている状態で、それでいて完全に内包関係が成立していたりはしなかったりする状況において、各テキストフレームが重なっている状況を2つの要素がペアになったリストで表現します。

先の図(↑)のような場合の実行結果は、

–> {{1, 2}, {3, 4}, {4, 5}}

のようになります。

テキストフレーム同士の重なり合い(衝突)の検出は……矩形Aと矩形Bの重なり合いの検出を考えた場合に(↓)、

indn2.png

矩形Aの座標の中に、矩形Bの各頂点座標が「含まれる」かどうかを判定すればよいので、条件文だけで判定可能です。

判定ロジック自体はInDesign以外のアプリケーションに対しても同様に利用できますが、矩形(テキストフレーム)の座標を求めたときに、InDesign特有の変態仕様で座標をY1, X1, Y2, X2の順で返してくることに合わせているため、座標値をY1,X1,Y2,X2のセットで扱っています。この点に気をつければ、他のアプリ用に変更することは難しくありません。

スクリプト名:各テキストフレームの衝突ペアを作成
–CS3〜CS6あたりまでアプリ名を書き換えれば動くはず
tell application “Adobe InDesign CS3″
  tell active document
    –テキストフレームを取得する
    
set aList to every text frame
    
    
–全テキストフレームのgeometric bounds=(Y1,X1,Y2,X2)を取得する    
    
set gList to {}
    
repeat with i in aList
      set the end of gList to geometric bounds of i
    end repeat
    
  end tell
end tell

–各テキストフレームの衝突ペアを作成
set gRes to collisionCheckByGeometricBounds(gList) of me
–> {{1, 2}, {3, 4}, {4, 5}}

–Geometric Boundsに着目して、衝突しているアイテムのペア(2つ)を返す
–ただし、3つ以上の衝突が発生しているケースは想定していない
on collisionCheckByGeometricBounds(gList)
  
  
set colList to {} –衝突検出したもののアイテム番号を追加
  
set colPairList to {} –衝突検出したアイテム番号のペアリストを追加
  
  
–チェック対象(1)を順次ループしてチェック
  
set itemCount to 1
  
repeat with ii in gList
    
    
–(1)に対していずれかの頂点座標が含まれているかどうか、チェックを総当たりで行う
    
set itemCount2 to 1
    
repeat with i in gList
      
      
–同じアイテム同士で衝突検出しても無意味なので、そのケースを除外する
      
if itemCount is not equal to itemCount2 then
        
        
–すでに、衝突検出したアイテム番号のリストに入っているものは処理から除外する
        
if itemCount2 is not in colList then
          set {y1, x1, y2, x2} to i
          
set posList to {{y1, x1}, {y1, x2}, {y2, x1}, {y2, x2}} –各頂点座標のリスト
          
          
set aRes to retIncludedPoint(ii, posList) of me –指定矩形内に、各頂点座標が入っているかどうかを判定する
          
          
–指定矩形内に、いずれかの頂点座標が入っていた(含まれていた)場合の処理
          
if aRes is not equal to {} then
            set the end of colPairList to {itemCount, itemCount2}
            
            
–同じペアが順番が入れ替わった状態で複数回検出されることを防ぐ({2,3}, {3,2}}など)
            
set the end of colList to itemCount2
            
set the end of colList to itemCount
          end if
          
        end if
      end if
      
      
set itemCount2 to itemCount2 + 1
    end repeat
    
    
set itemCount to itemCount + 1
  end repeat
  
  
return colPairList
  
end collisionCheckByGeometricBounds

–指定矩形内に含まれる座標をリストで返す
on retIncludedPoint(rangeList, posList)
  
  
set includedList to {}
  
  
set y1 to item 1 of rangeList
  
set x1 to item 2 of rangeList
  
set y2 to item 3 of rangeList
  
set x2 to item 4 of rangeList
  
  
repeat with i in posList
    
    
set targY1 to item 1 of i
    
set targX1 to item 2 of i
    
    
if (x1 targX1) and (x2 targX1) and (y1 targY1) and (y2 targY1) then
      set the end of includedList to (contents of i)
    end if
    
  end repeat
  
  
return includedList
  
end retIncludedPoint

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

2013/03/13 入れ子ではないリストの昇順、降順ソート(超高速版)

入れ子ではないただのリスト(1次元配列)を、昇順・降順ソートを超高速に行うAppleScriptのサブルーチンです。

前に投稿していただいた高速ソートルーチンを、サンプルのまま整備していなかったので、降順のものを作っておきました。

あとは、日常的には入れ子のリスト(2次元配列)をソートするものが絶対に不可欠なので、このあたりを整備しておく必要があります。

ソートルーチンやリストの各種操作ルーチンを整備しまくっておいたおかげで、よほどのことがなければFileMaker Proなどのデータベースを併用する必要は感じません。

(AppleScriptを覚えて)最初のうちは、FileMaker ProやJedit上の機能におんぶにだっこのScriptを組む気楽さから、とてもこれらのアプリケーションなしで過ごすことはできなかったものですが(とくにソート)……逆にいえば、自分の作るプログラムがJeditやFileMaker Proなしの環境では動かないケースが多くなってしまうわけで、脱FileMaker Proのためにルーチンを整備しはじめたという側面もあります。

スクリプト名:入れ子ではないリストの昇順、降順ソート
set aList to {1, 2, 3, 4, 5}

set bList to shellSortDescending(aList) of me
–> {5, 4, 3, 2, 1}

set cList to shellSortAscending(aList) of me
–> {1, 2, 3, 4, 5}

–入れ子ではないリストの昇順ソート
on shellSortAscending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortAscending

–入れ子ではないリストの降順ソート
on shellSortDescending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) < temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortDescending

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

2013/03/13 共通項目をキーにしてリスト内の項目を統合する v2

入れ子になったリストの、共通番号を持つもの同士を連結して統合するAppleScriptです。

たとえば、

  {{3, 4}, {4, 5}, {1, 2}, {2, 7}, {8, 9}}

といったリストが存在していたとします。各リストは2つの要素のペアで構成されており、その中には番号が重複しているものがあります。

 {3, 4}, {4, 5}

は、「4」という数字が共通して存在しています。なので、ここはいっそ、

 {3, 4, 5}

として2つの項目を統合できてしまったほうが便利……な場合があります。

そこで、冒頭のリストを本AppleScriptにかけると、

 {{3, 4, 5}, {1, 2, 7}, {8, 9}}

のように共通項をキーにして、共通項を持つアイテム同士を統合します。

ind1.png

何のためにこんなものを作ったかといえば、InDesignのドキュメント上にテキストフレームが重なって配置されている状態を検出したあとで、これらが重なった状態を解消するために、重なったテキストフレーム同士をまとめて(distributeコマンドで重ならないように再配置する)処理をするためです。

スクリプト名:リストのつながり同士を連結する v2
set aList to {{3, 4}, {4, 5}, {1, 2}, {2, 7}, {8, 9}}

set aaList to connectListItem(aList) of me
–> {{3, 4, 5}, {1, 2, 7}, {8, 9}}

–共通項目をキーにしてリスト内の項目を統合する
on connectListItem(aList)
  set bList to {}
  
set dList to {}
  
  
repeat with i from 1 to (length of aList)
    set con1 to contents of item i of aList
    
    
repeat with ii from 1 to (length of aList)
      if ii is not equal to i then
        set con2 to contents of item ii of aList
        
        
        
repeat with i3 in con1
          set ii3 to contents of i3
          
          
repeat with i4 in con2
            set ii4 to contents of i4
            
            
if ii3 = ii4 then
              
              
set aTmpList to (contents of item i of aList) & (contents of item ii of aList)
              
set cList to removeDuplicates(aTmpList) of me –重複削除
              
set cList to shellSort5(cList) of me –ソート
              
              
if {cList} is not in bList then
                set the end of bList to cList
                
set the end of dList to {i, ii}
              end if
              
            end if
          end repeat
          
        end repeat
        
      end if
    end repeat
    
  end repeat
  
  
set ddList to FlattenList(dList) of me
  
  
repeat with i from 1 to (length of aList)
    set ii to i as string
    
if ii is not in ddList then
      set j to contents of item i of aList
      
set the end of bList to j
    end if
  end repeat
  
  
return bList
  
end connectListItem

–リスト項目のユニーク化(重複削除)
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–プレーンなAppleScriptベースでは最速ソート
on shellSort5(aSortList) – スクリプトオブジェクトを使用
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSort5

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

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

2013/03/10 Safariで指定User Agentで指定URLをオープン

Safariで指定ユーザーエージェント名で、指定のURLをオープンするAppleScriptです。

Safari 6.0.3+OS X 10.8.2で動作確認を行っています。GUI Scriptingを用いているので、あらかじめシステム環境設定の「アクセシビリティ」で「補助装置にアクセスできるようにする」のチェックを入れておいてください。

# GUI側からコントロールしているので、OSおよびSafariのバージョンに依存しています

最初に、ダミーURLをオープンしてから、ユーザーエージェント名を指定して、ターゲットURLをオープンすることになります。

いきなりターゲットのURLをデフォルト設定のままオープンしてしまうと、クッキーなどがデフォルトのブラウザ(Safari)に合わせて書き換えられる可能性があるので、こういう順序で処理させてみました。

スクリプト名:Safariで指定User Agentで指定URLをオープン
–Safari v6.0.3で指定可能なUser Agentの一覧(「その他」で具体的に指定することも可能
set uaList to {“Safari 6.0.3 ― Mac”, “Safari 5.1.7 ― Windows”, “Safari iOS 5.1 ― iPhone”, “Safari iOS 5.1 ― iPod touch”, “Safari iOS 5.1 ― iPad”, “Internet Explorer 9.0″, “Internet Explorer 8.0″, “Internet Explorer 7.0″, “Google Chrome 19.0 ― Mac”, “Google Chrome 19.0 ― Windows”, “Firefox 11.0 ― Mac”, “Firefox 11.0 ― Windows”, “Opera 11.62 ― Mac”, “Opera 11.62 ― Windows”}

–ためしに、リストからUser Agentを選択(普通、こんなかったるい処理はしませんが、動作テスト用)
set aRes to choose from list uaList with prompt “ユーザーエージェントを選択”
if aRes = false then return

set anUserAgent to contents of first item of aRes

set aURL to “http://piyocast.com/as” –オープン対象のURL
set bRes to openAurlBySpecifiedUserAgent(anUserAgent, aURL) of me
–> true(オープンできたとき)
–>false(オープンできなかったとき)

–指定User Agentで指定URLをオープン
on openAurlBySpecifiedUserAgent(aUAstr, aURL)
  
  
–最初にURLをオープンしておいて、あとでURLを変更する
  
set dummyURL to “http://www.google.co.jp” –どこか、適当なページ。ターゲットのURL以外ならどこでもいい? 
  
tell application “Safari”
    try
      make new document with properties {URL:dummyURL}
    on error
      return false
    end try
  end tell
  
  
activate application “Safari”
  
  
tell application “System Events”
    tell process “Safari”
      
      
–指定メニュー項目をクリック
      
repeat 10 times
        try
          click menu item aUAstr of menu 1 of menu item 2 of menu 1 of menu bar item 8 of menu bar 1
        on error
          return false
        end try
        
        
–本当に設定できたかどうか確認
        
set aRes to selected of menu item aUAstr of menu 1 of menu item 2 of menu 1 of menu bar item 8 of menu bar 1
        
if aRes is not equal to false then
          return false
        else
          exit repeat
        end if
        
        
delay 1
        
      end repeat
    end tell
  end tell
  
  
–ひらいておいたウィンドウで指定URLをオープンする
  
tell application “Safari”
    try
      tell document 1
        set URL to aURL
      end tell
    on error
      return false
    end try
  end tell
  
  
return true
  
end openAurlBySpecifiedUserAgent

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

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

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

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

safari0.png

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

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

safari1.png

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

safari2.png

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

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

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

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

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

2013/03/07 数値テキストに、指定桁ごとにセパレータ文字を入れる

数値のテキストに、指定桁ごとにセパレータ文字を入れるAppleScriptです。

こんな機能を有しない処理系が他にあるのか疑問ですが、とにかくないので……普通はdo shell script経由で他の言語処理系を呼び出すところですが、スクラッチからAppleScriptで組んでしまいました。

  ”123456789″ → ”123,456,789″

のように、3桁区切りにカンマを入れたりするためのものです。入れる桁間隔、セパレータ文字も指定可能です。

スクリプト名:数値テキストに、指定桁ごとにセパレータ文字を入れる
set aNumStr to “12345″
set aRes to addSeparatorToString(aNumStr, 3, “,”) of me
–> “12,345″

–数値テキストに、指定桁ごとにセパレータ文字を入れる
on addSeparatorToString(aNumStr, sepDigit, sepChar)
  set aList to characters of aNumStr
  
set rList to reverse of aList
  
set maxLen to (length of rList)
  
  
set outList to {}
  
set aCount to 1
  
set bCount to 1
  
  
repeat with i in rList
    set j to contents of i
    
set the beginning of outList to j
    
    
if aCount = sepDigit then
      if bCount < maxLen then
        set the beginning of outList to sepChar
      end if
      
set aCount to 0
    end if
    
    
set aCount to aCount + 1
    
set bCount to bCount + 1
  end repeat
  
  
set outStr to outList as string
  
end addSeparatorToString

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

2013/03/06 AppleのiBookstoreが日本国内向けにコンテンツ販売を開始

AppleのiBookstoreが日本国内向けにコンテンツ販売を開始しました。基本的には、日本国内の出版社がコンテンツを提供しているようです。

「ようです」といっているのは、よく見るとAll About編集部のコンテンツなども(ビジネス書/コンピュータ)見られるからで……何が基準になっているのかよく分らないのですが(ISBNを取得している会社だけ?)、さらに裾野を広げるべく無料コンテンツや個人ベースのコンテンツも販売できるようになるのでは、などと期待しています。

待っているだけでは話にならないので、以前に書いていたものをベースに……

ib1.png

ib2.png

ib3.png

ib4.png

ib5.png

Pages上で書きためているものがあります。iBooksotre上では、コンテンツのバージョンアップが可能なので、新しいOSが登場して、内容を修正する必要がある場合にも対応できるので安心です。

さて……個人でもiBookstore上で本を売れるようになる時代は、日本にもやってくるのかどうか?

2013/03/05 AppleのAppleScript関連ドキュメントの個人的な翻訳サイト

ネット上で探し物をしていたら、マニュアルなどの個人的な日本語翻訳を掲載しているサイトを見つけました。

mytrans マニュアル等の個人的な翻訳

「AppleScript関連の本がなくて……」

と、仰せの向きが多いようなので、こうしたサイトはたいへん参考になるのではないでしょうか?

2013/03/05 AppleScriptの処理中断

AppleScriptの処理を途中で強制停止/強制終了/緊急停止したい場合の操作方法などについてです。

たとえば、こんな……無限ループ処理なんかを書いて実行してみると……

ase1.png

当然のように処理は終わりません。

ase2.png

そういう場合には、Command-.(ピリオド)をキー入力して止めるか、AppleScriptエディタの「停止」ボタンをクリックすることになります。

ase3.png

処理を停止できました。こんなふうに素直に停止できればラッキーですが、そんな風に止められるケースは多くないので、仕方なくAppleScriptエディタごと強制終了をさせることが多いです。

ase4.png

でも、プログラムが迷走状態に入ってしまった場合に、どうしても止めたい……というケースはあると思います(もちろん、プロはそんなケースも考慮して、処理が破綻しないように処理を組み立てるのが普通ですが、、、)。

そこで、何かの状態の変化をつねに監視して、変化が起こったら停止させるような処理をループ部分に仕込んでおくとか、そういう対処は、初心者用にはおすすめできるのではないかと思います(自分も、ASをはじめたころに、そういう処理に凝った時期がありました)。

ここにあげる例は、「デスクトップ上に『プログラム強制停止用フォルダ』という名前のフォルダが存在したら処理中止」を行うというものです。あくまで一例であって、こうするのが普通だとか、こうしなければいけないといったことはありません。例です。

ase5.png

Message(旧称iChat)のステータスメッセージの監視など、いろいろ監視先はあると思うものの……Finderはだいたい単独でクラッシュし続けていることは少ないですし、ファイルやフォルダの存在確認ならさほどCPUパワーも消費しないはずなので(遅くならないだろう、と)。

さらに、ネットワークごしに他のマシンからフォルダを作成することもできますし、最悪の場合でもTerminalからログインしてフォルダを作成するようなことも可能です。そこまで考慮するなら、監視するフォルダ名は英語の名称がよいということになるでしょう。

いずれにしても、迷走しないように無限ループに入りそうなところにはループ回数カウンタでも付けておいて、20回処理したら終了とか「先手を打って」おくのが普通です。

スクリプト名:強制停止用処理
set dtPath to (path to desktop) as string

repeat
  tell application “Finder”
    tell folder dtPath
      set stopExists to exists of folder “プログラム強制停止用フォルダ”
      
if stopExists = true then return
    end tell
  end tell
  
  
–ここからメイン処理
  
end repeat

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

2013/03/04 指定日時の午前午後を返す

指定の日時(dateオブジェクト)から午前/午後を返すAppleScriptです。

……というよりも、AppleScriptにそんな機能はないので、dateオブジェクトから「時」を数値で取得して、条件文で判定するということになります。

AppleScript名:指定日時の午前午後を返す_10.10
set aDate to current date –現時刻
set ampmRes to retAMPM(aDate) of me

–指定日時の午前/午後を返す
on retAMPM(aDate as date)
  set a to hours of aDate –「時」を取得
  
  
if a 0 and a < 12 then
    return "AM"
  else
    return "PM"
  end if
  
end retAMPM

★Click Here to Open This Script